首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >等待事件处理程序的执行

等待事件处理程序的执行
EN

Stack Overflow用户
提问于 2015-02-26 19:41:49
回答 2查看 817关注 0票数 3

我有一个数据存储库,它为我的应用程序的模型提供了一个持久层。对磁盘的所有访问都是异步的,所以我的整个持久层都是使用异步/等待编写的。我的数据存储库允许其他模块订阅数据中的更改:

代码语言:javascript
复制
 public event EventHandler<Journaling.DataChangeEventArgs> DataChanged;   
 protected void OnDataChanged(Journaling.Action a)
 {
      if (DataChanged != null)
      {
           DataChanged(this, new Journaling.DataChangeEventArgs(a));
      }
 }

当我告诉存储库删除其他对象引用的对象时,它也会删除这些其他对象。

代码语言:javascript
复制
 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;
      }
 }

此设置在某种程度上有效,但在删除引用其他对象的对象时,我遇到了问题。考虑一下这个例子:

代码语言:javascript
复制
Object X 
  Object A1: references X
  Object A2: references X
  Object A3: references X

我有一个记录器模块,它订阅数据存储库中的更改并将它们输出到文件中。记录器有时需要从存储库中获取其他数据,以使其输出具有可读性。删除对象X时的日志应该是:

代码语言:javascript
复制
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中事件处理程序的执行。这样,我就可以确保记录器在下一个对象从存储中删除之前已经完成了它的工作。

有人能告诉我该怎么做吗?我考虑过使用信号量,但这会破坏数据存储库和记录器之间的松耦合。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2015-02-26 21:11:46

我有一个关于“异步事件”的博客文章。通常,我建议使用“延迟”,这是Windows中的一个概念。

例如,使用我的DeferralManager库中的AsyncEx类型,您可以首先启用事件参数类型以支持延迟:

代码语言:javascript
复制
public class DataChangeEventArgs : EventArgs
{
  private readonly DeferralManager _deferrals;

  public DataChangeEventArgs(DeferralManager deferrals, Journaling.Action a)
  {
    _deferrals = deferrals;
  }

  public IDisposable GetDeferral()
  {
    return deferrals.GetDeferral();
  }
}

然后引发这样的事件:

代码语言:javascript
复制
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,则消费代码可以使用延迟。

代码语言:javascript
复制
async void DataChangedHandler(object sender, Journaling.DataChangeEventArgs args)
{
  using (args.GetDeferral())
  {
    // Code can safely await in here.
  }
}
票数 4
EN

Stack Overflow用户

发布于 2015-02-26 20:01:10

因为您的处理程序是异步的,并且调用这些处理程序的类型需要知道它们何时完成,这些处理程序需要返回一个Task而不是void

在调用此处理程序时,需要获取调用列表并调用每个单独的方法,而不是一次调用所有方法,因为您需要能够获得所有的返回值。

您还需要修改OnDataChanged的签名,以返回一个Task,以便调用者能够知道它何时完成。

代码语言:javascript
复制
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);

}
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/28751338

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档