扩展方法线程安全吗?

发布于 2024-10-10 17:02:13 字数 586 浏览 6 评论 0原文

这个扩展方法线程安全吗?

   public static class Extensions
   {
      public static void Raise<T>(this EventHandler<T> handler,
        object sender, T args) where T : EventArgs
      {
         if (handler != null) handler(sender, args);
      }
   }

或者我需要将其更改为这个?

   public static class Extensions
   {
      public static void Raise<T>(this EventHandler<T> handler,
        object sender, T args) where T : EventArgs
      {
         var h = handler;
         if (h!= null) h(sender, args);
      }
   }

Is this Extension method thread safe?

   public static class Extensions
   {
      public static void Raise<T>(this EventHandler<T> handler,
        object sender, T args) where T : EventArgs
      {
         if (handler != null) handler(sender, args);
      }
   }

or do I need to change it to this?

   public static class Extensions
   {
      public static void Raise<T>(this EventHandler<T> handler,
        object sender, T args) where T : EventArgs
      {
         var h = handler;
         if (h!= null) h(sender, args);
      }
   }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

泪冰清 2024-10-17 17:02:13

你发现了一个有趣的漏洞,它把每个人都绊倒了。不,它不是线程安全的。

虽然它看起来像EventHandler<>引用是通过方法参数复制的,这不是运行时发生的情况。扩展方法需要内联,就像常规实例方法一样。事实上,由于它太小,它可能会被内联。没有副本,必须自己制作一份。

You found an interesting loop hole, it tripped everybody up. No, it is not thread-safe.

While it looks like the EventHandler<> reference is copied through the method argument, this is not what happens at runtime. Extension methods are subject to being inlined, just like a regular instance method. In fact, it is extremely likely to get inlined since it is so small. There's no copy, you have to make one yourself.

清醇 2024-10-17 17:02:13

这两个版本都不是线程安全的,具体取决于“线程安全”的含义。考虑您的第二个版本:

   var h = handler;          
   if (h!= null) 
       h(sender, args);

“处理程序”是某个字段的副本,其中包含不可变的委托。假设在空检查后该字段在另一个线程上突变为“空”。在这种情况下,您的代码不会崩溃,因为您已经复制了原始非空值。但仅仅不崩溃并不能使程序线程安全。一个没有崩溃但仍然产生错误结果的程序仍然不是线程安全的。

假设当另一个线程将事件字段设置为空时,它也改变了先前内容正确运行所需的某些状态。现在,您将运行一个事件处理程序,该处理程序取决于刚刚在另一个线程上发生变化的状态;您正在运行一个陈旧事件处理程序。

没有简单的方法可以防止这个问题;如果这就是您所处的情况,那么您将必须非常仔细地设计线程逻辑才能处理这种情况。

Neither version is threadsafe, depending on what you mean by "threadsafe". Consider your second version:

   var h = handler;          
   if (h!= null) 
       h(sender, args);

"handler" is a copy of some field that has an immutable delegate in it. Suppose that field is mutated to "null" on another thread after the null check. Your code does not crash in that case, because you've made a copy of the original not-null value. But merely not crashing does not make the program thread safe. A program that doesn't crash but still produces the wrong results is still not threadsafe.

Suppose when the other thread set the event field to null, it also mutated some state that the previous contents needed to run correctly. You are now going to run an event handler that depends on state that was just mutated on another thread; you're running a stale event handler.

There is no easy way to protect against this problem; if that's the situation you're in, then you're going to have to design your threading logic extremely carefully in order to deal with the situation.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文