標籤:style blog http color os 使用 io ar for
微軟的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】同步執行非同步代碼