• 【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

  • 相关阅读:
    硬盘安装Win 7系统Windows 7 系统硬盘安装教程(图解)
    修改phpMyAdmin导入SQL文件的大小限制
    金三银四面试季节之Java 核心面试技术点
    2015年校园招聘12家IT公司面试体验
    正则表达式小结
    【译文】NginScript – 为什么我们要实现自己的JS引擎?
    经典算法合集
    【高级JSE技术】线程池
    【高性能服务器】Tomcat剖析
    【高性能服务器】Nginx剖析
  • 原文地址:https://www.cnblogs.com/bomo/p/3945761.html
Copyright © 2020-2023  润新知