【WP8】同步执行异步代码

微软的StorageFile只支持异步的方式进行文件操作,我之前也封装过一个StorageHelper,但是当所有的方法都是异步的时候也带来一些问题

  1、比如我们不能在构造函数调用异步代码(等待),

  2、比如我们在离开App的时候我们需要对数据进行快速的保存(在事件中),这个时候就不适合用异步了,异步可能会导致保存失败,因为App已经不在前台了

最近就遇到了一个这样的需求

  我封装了一个SqliteHelper类,提供了一些数据库操作的一些常用方法,在数据库使用之前,需要保证数据库路径的文件夹是存在的,如果不能存在,则会抛出异常,所以在SqliteHelper的构造函数判断该文件夹是否存在,如果不存在,则创建该文件夹,但是StorageHelper不支持同步方法(这里不使用IsolatedStorage),我们需要保证在StorageHelper在构造完成时,保证文件夹存在

    public SqliteHelper(string dbPath)
    {
        this.dbPath = Path.Combine(ApplicationData.Current.LocalFolder.Path, dbPath);
        var directory = Path.GetDirectoryName(dbPath);
        if (directory != null)
        {
            //TODO:构造函数不支持await关键字
            //await StorageHelper.Instance.CreateDirectoryAsync(directory);
        }
    }

  满足上面需求可以有两个方法:

  1、通过一个静态异步方法来构造SqliteHelper的实例,把异步操作放到异步方法里面,但是这种方法会导致不能使用依赖注入IoC

    private SqliteHelper(string dbPath)
    {
        this.dbPath = Path.Combine(ApplicationData.Current.LocalFolder.Path, dbPath);
    }

    public static async Task<SqliteHelper> GetSqliteHelper(string dbPath)
    {
        var sqliteHelper = new SqliteHelper(dbPath);
        var directory = Path.GetDirectoryName(dbPath);
        if (directory != null)
        {
            await StorageHelper.Instance.CreateDirectoryAsync(directory);
        }
        return sqliteHelper;
    }

 

  2、让异步方法同步执行

    我们首先定义一个异步执行Helper,提供异步方法的同步(摘自后面的参考文章)

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace TestDemo
{
    public static class AsyncInline
    {
        public static void Run(Func<Task> item)
        {
            var oldContext = SynchronizationContext.Current;
            var synch = new ExclusiveSynchronizationContext();
            SynchronizationContext.SetSynchronizationContext(synch);
            synch.Post(async _ =>
            {
                try
                {
                    await item();
                }
                catch (Exception e)
                {
                    synch.InnerException = e;
                    throw;
                }
                finally
                {
                    synch.EndMessageLoop();
                }
            }, null);
            synch.BeginMessageLoop();
            SynchronizationContext.SetSynchronizationContext(oldContext);
        }
        public static T Run<T>(Func<Task<T>> item)
        {
            var oldContext = SynchronizationContext.Current;
            var synch = new ExclusiveSynchronizationContext();
            SynchronizationContext.SetSynchronizationContext(synch);
            T ret = default(T);
            synch.Post(async _ =>
            {
                try
                {
                    ret = await
                    item();
                }
                catch (Exception e)
                {
                    synch.InnerException = e;
                    throw;
                }
                finally
                {
                    synch.EndMessageLoop();
                }
            }, null);
            synch.BeginMessageLoop();
            SynchronizationContext.SetSynchronizationContext(oldContext);
            return ret;
        }

        private class ExclusiveSynchronizationContext : SynchronizationContext
        {
            private bool done;
            public Exception InnerException { get; set; }
            readonly AutoResetEvent workItemsWaiting = new AutoResetEvent(false);
            readonly Queue<Tuple<SendOrPostCallback, object>> items =
             new Queue<Tuple<SendOrPostCallback, object>>();

            public override void Send(SendOrPostCallback d, object state)
            {
                throw new NotSupportedException("We cannot send to our same thread");
            }
            public override void Post(SendOrPostCallback d, object state)
            {
                lock (items)
                {
                    items.Enqueue(Tuple.Create(d, state));
                }
                workItemsWaiting.Set();
            }
            public void EndMessageLoop()
            {
                Post(_ => done = true, null);
            }
            public void BeginMessageLoop()
            {
                while (!done)
                {
                    Tuple<SendOrPostCallback, object> task = null;
                    lock (items)
                    {
                        if (items.Count > 0)
                        {
                            task = items.Dequeue();
                        }
                    }
                    if (task != null)
                    {
                        task.Item1(task.Item2);
                        if (InnerException != null) // the method threw an exeption
                        {
                            throw new AggregateException("AsyncInline.Run method threw an exception.",
                             InnerException);
                        }
                    }
                    else
                    {
                        workItemsWaiting.WaitOne();
                    }
                }
            }
            public override SynchronizationContext CreateCopy()
            {
                return this;
            }
        }
    }
}

    接下来我们就可以像同步方法一样使用异步方法了

    public class TestClass
    {
        public int Id { get; set; }

        public TestClass()
        {
            //这里调用一个异步方法,等待异步方法执行返回
            Id = AsyncInline.Run(GetIdAsync);

            //不支持await
            //await GetIdAsync();
        }

        /// <summary>
        /// 异步方法
        /// </summary>
        public async Task<int> GetIdAsync()
        {
            //TODO:这里执行模拟自定义操作
            await Task.Delay(1000);

            return 10;
        }
    }

 

 

参考文章:

  http://social.msdn.microsoft.com/Forums/en-US/163ef755-ff7b-4ea5-b226-bbe8ef5f4796/is-there-a-pattern-for-calling-an-async-method-synchronously?forum=async

  http://stackoverflow.com/questions/8145479/can-constructors-be-async

 

个人能力有限,如果有更好的实现,可以给我留言

转载请注明出处:http://www.cnblogs.com/bomo/p/3945761.html

 

你可能感兴趣的:(同步)