【问题标题】:Multi-threaded concerns when coding event accessors编写事件访问器时的多线程问题
【发布时间】:2016-02-22 17:23:57
【问题描述】:

我正在寻找其他信息,了解为什么 Microsoft 建议在编写事件访问器语法时锁定对象。微软的代码示例如下所示,建议链接到。

我理解关于锁定一段代码以控制多线程访问它的一般概念,但是我正在寻找在 Microsoft 示例的上下文中编写自定义事件访问器逻辑时这些问题发挥作用的具体原因显示。

以下示例展示了如何实现自定义添加和删除 事件访问器。虽然你可以替换里面的任何代码 访问者,我们建议您在添加或 删除一个新的事件处理方法。

event EventHandler IDrawingObject.OnDraw
    {
        add
        {
            lock (PreDrawEvent)
            {
                PreDrawEvent += value;
            }
        }
        remove
        {
            lock (PreDrawEvent)
            {
                PreDrawEvent -= value;
            }
        }
    }

~通过https://msdn.microsoft.com/en-us/library/bb882534.aspx?f=255&MSPPError=-2147217396

【问题讨论】:

  • 能否在引发事件的同时移除事件句柄?会猜测这可能会导致空引用异常。
  • 我不是 100% 确定,但我认为这是因为事件基本上是多播委托。因此,当您添加/删除处理程序时,实际上会创建一个 new 委托。所以,它实际上是一个两步操作:生成新的委托,并分配给现有的变量。如果多个线程同时(取消)订阅,则会引入一个竞争条件窗口。
  • 进一步说明,由于上述原因,我认为锁定您正在修改的事件将是不好的形式。拥有一个专用的锁对象似乎更明智。
  • @C.Knight - 这是一种可能性,尽管它与上述逻辑没有任何关系。您应该始终将事件分配给局部变量,然后在尝试调用它之前检查它是否为空。
  • += 不是原子的。这是一个 3 步过程,它使您的 PreDrawEvent 处于无效状态,直到所有 3 个步骤完成:(1) 将现有 PreDrawEvent 复制到寄存器中,(2) 附加到寄存器中的事件,(3) 将新附加的列表复制回变量...由exactly the guy you'd expect在这里回答...

标签: c# multithreading events event-handling


【解决方案1】:

只有 MSDN 文章的作者才能为您提供关于文章措辞的明确答案。

但是:在我看来,建议的主要原因是代码几乎总是使用编译器提供的事件访问器方法。这些一直是为了 100% 线程安全的,并且随着最近编译器的变化(我认为是 C# 4,但我不记得了),它们实际上是。

我认为,使默认实现线程安全的原因是不言自明的:这样做涉及相当低的成本,并且事件访问器方法中对线程安全的需求足够频繁,以至于迫使开发人员实现自己的每次需要线程安全的访问器都是不合理的。

因此,鉴于默认实现是线程安全的,这意味着事件的消费者(他们通常无法随时访问事件的源代码)将习惯于假设事件访问器是始终线程安全。违反此假设可能会导致代码中出现错误。

底线:如果您 100% 确定您的事件只会在单个线程中被访问,或者至少以线程安全的方式访问,那么您可以在不为访问器方法添加显式线程安全的情况下摆脱困境。问题是,达到这个 100% 的确定性是有问题的。几乎不可能预测一段特定的代码将被如何使用,尤其是在更远的未来。

代码可以存活很长时间。最好确保它可以处理抛出的内容,尤其是当代码的未来客户完全有理由假设代码可以处理它时。


顺便说一句:虽然 MSDN 显示锁定事件字段本身,但这对我来说似乎有问题。更新该字段的那一刻,任何当前持有的锁都不会阻止随后执行的代码进入锁,即使锁本身还没有退出。由于对字段的读取和写入顺序错误,某些平台上可能存在字段可见性问题;这可能导致两个随后执行的线程看到不同的锁值,然后同时进入受保护的部分。

不要介意使用公开可用值进行锁定的更普遍的问题。关于该特定主题存在一些争论,但我更倾向于只使用私有值进行锁定。 IE。不要使用事件字段的当前值锁定(因为它是可变的),也不要使用this 锁定(因为它是公开的)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-02-01
    • 2021-11-11
    • 2023-03-21
    • 1970-01-01
    • 2012-05-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多