[英]Awaiting the execution of event handlers
我有一個數據存儲庫,為我的應用程序的模型提供持久層。 對光盤的所有訪問都是異步的,因此我的整個持久層都是用async / await編寫的。 我的數據存儲庫允許其他模塊訂閱數據中的更改:
public event EventHandler<Journaling.DataChangeEventArgs> DataChanged;
protected void OnDataChanged(Journaling.Action a)
{
if (DataChanged != null)
{
DataChanged(this, new Journaling.DataChangeEventArgs(a));
}
}
當我告訴存儲庫刪除其他對象引用的對象時,它也會刪除這些其他對象。
public async Task<bool> DeleteAsync(Models.BaseModel model)
{
await DeleteDependentModelsAsync(model).ConfigureAwait(false);
if (await connection.DeleteAsync(model).ConfigureAwait(false) != 1)
return false;
else
{
var deleteAction = new Journaling.DeleteAction(model);
OnDataChanged(deleteAction);
return true;
}
}
此設置在某種程度上有效,但在刪除引用其他對象的對象時遇到問題。 考慮這個例子:
Object X
Object A1: references X
Object A2: references X
Object A3: references X
我有一個記錄器模塊,它訂閱數據存儲庫中的更改並將它們輸出到文件中。 記錄器有時需要從存儲庫中獲取其他數據,以使其輸出可讀。 刪除對象X時的日志應為:
A1 deleted (parent: X, more information contained in X)
A2 deleted (parent: X, more information contained in X)
A3 deleted (parent: X, more information contained in X)
X deleted
問題是OnDataChange不等待事件處理程序的執行。 因此,數據存儲庫在記錄器的事件處理程序甚至被調用一次之前刪除A1-A3然后X. 但事件處理程序必須獲取有關X的一些信息,這是不可能的,因為數據存儲庫已經刪除了X.
我不得不等待在OnDataChanged中執行事件處理程序。 這樣我可以確保記錄器在從存儲中刪除下一個對象之前已完成其工作。
任何人都可以在正確的方向上暗示我如何做到這一點? 我考慮過使用信號量,但這會破壞我在數據存儲庫和記錄器之間的松散耦合。
我有一篇關於“異步事件”主題的博客文章 。 通常,我建議使用“deferrals”,這是Windows Store API中的一個概念。
例如,使用DeferralManager
類型從我AsyncEx庫中,可以先使你的事件參數類型,支持延期:
public class DataChangeEventArgs : EventArgs
{
private readonly DeferralManager _deferrals;
public DataChangeEventArgs(DeferralManager deferrals, Journaling.Action a)
{
_deferrals = deferrals;
}
public IDisposable GetDeferral()
{
return deferrals.GetDeferral();
}
}
然后你舉起這樣的事件:
protected Task OnDataChangedAsync(Journaling.Action a)
{
var handler = DataChanged;
if (handler == null)
return Task.FromResult<object>(null); // or TaskConstants.Completed
var deferrals = new DeferralManager();
var args = new Journaling.DataChangeEventArgs(deferrals, a);
handler(args);
return deferrals.SignalAndWaitAsync();
}
如果需要使用await
則使用代碼可以使用延遲:
async void DataChangedHandler(object sender, Journaling.DataChangeEventArgs args)
{
using (args.GetDeferral())
{
// Code can safely await in here.
}
}
由於處理程序是異步的,並且調用這些處理程序的類型需要知道它們何時完成,因此這些處理程序需要返回Task
而不是void
。
調用此處理程序時,您需要獲取調用列表並調用每個單獨的方法,而不是一次調用所有方法,因為您需要能夠獲取所有返回值。
您還需要更改OnDataChanged
的簽名以返回Task
以便調用者能夠知道它何時完成。
public event Func<Journaling.Action, Task> DataChanged;
protected Task OnDataChanged(Journaling.Action a)
{
var handlers = DataChanged;
if (handlers == null)
return Task.FromResult(0);
var tasks = DataChanged.GetInvocationList()
.Cast<Func<Journaling.Action, Task>>()
.Select(handler => handler(a));
return Task.WhenAll(tasks);
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.