编码事件访问器时的多线程问题

时间:2016-02-22 17:23:57

标签: c# multithreading events event-handling

我正在寻找有关Microsoft建议在编写事件访问器语法时锁定对象的原因的其他信息。 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

1 个答案:

答案 0 :(得分:1)

只有MSDN文章的作者可以就文章的措辞为您提供明确的答案。

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

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

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

底线:如果您100%确定您的事件只能在一个线程中访问,或者至少以线程安全的方式访问,您可以在不向访问器方法添加显式线程安全的情况下离开。问题是,达到这种100%的确定性是有道理的;几乎不可能预测一段特定代码的使用方式,特别是未来人们谈论的更远。

代码可以在相当长的时间内存活。最好确保它可以处理它所引发的内容,特别是当代码的未来客户有充分的理由假设代码可以处理它时。


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

别介意使用公开可用值进行锁定的更一般问题。关于该特定主题存在一些争论,但我倾向于仅使用私有值进行锁定。即不要使用事件字段的当前值锁定(因为它是可更改的)并且不使用this锁定(因为它是公共的)。