首页 > 代码库 > 【WP8】同步执行异步代码

【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/3942750.html

 

【WP8】同步执行异步代码