【问题标题】:Threadsafe and exception catching events线程安全和异常捕获事件
【发布时间】:2014-07-04 10:05:25
【问题描述】:

我使用以下示例来执行事件:

try
{
    if (this.onClientConnected != null)
        this.onClientConnected(this, new EventArgs());
}
catch (Exception e)
{
    //log and handle exception
}

但这当然有一些缺点,比如如果一个订阅的委托抛出一个异常,其余的将不会被执行,因为我在多线程应用程序中使用它,在检查 null 和抛出有人可以取消订阅的事件之间,会抛出一个 null 异常.

现在我寻找了一种更好的方法来抛出这些事件并想出了这个:

Delegate[] methods = this.onClientConnected.GetInvocationList();
methods.ToList().ForEach(x =>
{
    try
    {
        x.DynamicInvoke(this, new EventReceivedEventArgs(Trigger.TriggerName, Trigger.InstanceID, Trigger.Parameters, Trigger.SourceIpAddress));
    }
    catch (Exception e)
    {
        //log and handle exception
    }
});

这是一种好方法还是有更好的方法来处理带异常的多线程事件?

【问题讨论】:

    标签: c# events


    【解决方案1】:

    是否可以通过从同一线程运行所有添加/删除属性访问,或者通过在正确的抽象级别使用锁来序列化所有订阅/取消订阅逻辑?见Stephen Toub's discussion on this topic

    我会将事件中的多播委托缓存到一个局部变量中,检查它,然后“调用”它。见Eric Lippert's discussion on the topic.

    考虑在任务中包装事件,如Igor Ostrovsky describes in this MSDN Article。这是 TPL 路线...

    例如(我正在引用):

    static Task<string> DownloadStringAsTask(Uri address) {
      TaskCompletionSource<string> tcs = 
        new TaskCompletionSource<string>();
      WebClient client = new WebClient();
      client.DownloadStringCompleted += (sender, args) => {
        if (args.Error != null) tcs.SetException(args.Error);
        else if (args.Cancelled) tcs.SetCanceled();
        else tcs.SetResult(args.Result);
      };
      client.DownloadStringAsync(address);
      return tcs.Task;
    }
    

    在返回的任务被评估的任何地方都可以观察到异常,例如使用 .Result 或使用 .Wait()。

    考虑通过同一线程汇集事件处理。例如,使用 .ContinueWith() 重载,该重载采用始终发布到 UI 线程的调度程序。

    最后,Rx 是事件的另一个绝佳替代品。

    【讨论】:

      猜你喜欢
      • 2011-05-25
      • 1970-01-01
      • 1970-01-01
      • 2011-07-07
      • 1970-01-01
      • 1970-01-01
      • 2013-01-07
      • 2015-08-31
      • 2013-12-14
      相关资源
      最近更新 更多