按正确的顺序从异步/等待函数触发事件

时间:2015-06-02 14:04:14

标签: c# events design-patterns asynchronous async-await

我在确保异步任务中的事件顺序时遇到问题。其他异步任务继承的类具有以下功能,并在构造函数参数中使用 EventHandlers(ExecutionProgress,ExecutionStarted,ExecutionCompleted)

public abstract Task Operation(IProgress<EventArgs> progress);

public virtual void Execute()
{
    ExecuteAsync()
}

private void ReportProgress(EventArgs args)
{
    if(ExecutionProgress != null) ExecutionProgress(this, args);
}
private async Task ExecuteAsync()
{
    if(ExecutionStarted != null) ExecutionStarted(this, EventArgs.Empty)
    await Operation(new Progress<EventArgs>(ReportProgress));
    if(ExecutionCompleted != null) ExecutionCompleted(this, EventArgs.Empty)

}

现在在我的一个类中,它继承自异步任务,我使用以下内容覆盖Operation:

public override async Task Operation(IProgress<EventArgs> progress)
{
    // run the synchronous function in another thread
    JobResults results = await Task.Run(() => worker.DoYourJob()); 
    progress.Report(results);
}

在尝试访问 JobResults 时,经常(但并非总是)运行此代码会在 ExecutionCompleted 事件处理程序中导致 NullReference异常。这是因为写入成员变量的 ExecutionProgress 事件通常在 ExecutionCompleted 之后因任何原因被触发。我认为标准没有说明事件排序,但我正在寻找一个很好的解决方案来确保确定性排序。 我只想在处理完所有ExecutionProgress事件后才触发ExecutionCompleted。

这里有什么好看的解决方案?在处理完所有进度报告事件之前,有没有办法等待

1 个答案:

答案 0 :(得分:3)

这些事件都按顺序解雇。如果您的事件处理程序是异步的,这似乎就是这种情况,那么触发事件的代码将在启动事件处理程序后立即继续执行,而不是在他们完成

如果触发事件的代码不需要继续,直到所有处理程序都已完成,那么您需要重新构造此类,以便事件处理程序有一些方法可以在完成时向此类指示。有许多方法可以做到这一点,从允许处理程序在完成时告诉它的事件中传递参数,或者让事件处理程序的签名返回Task而不是{{{ 1}}(在触发事件时你需要void这些任务。)