相关疑难解决方法(0)

C#事件和线程安全

UPDATE

从C#6开始,这个问题的答案是:

SomeEvent?.Invoke(this, e);
Run Code Online (Sandbox Code Playgroud)

我经常听到/阅读以下建议:

在检查事件之前,请务必复制事件null并将其触发.这将消除线程的潜在问题,其中事件变为null位于您检查null和触发事件的位置之间的位置:

// Copy the event delegate before checking/calling
EventHandler copy = TheEvent;

if (copy != null)
    copy(this, EventArgs.Empty); // Call any handlers on the copied list
Run Code Online (Sandbox Code Playgroud)

更新:我从阅读中了解到这可能还需要事件成员的优化,但Jon Skeet在他的回答中指出CLR不会优化副本.

但同时,为了解决这个问题,另一个线程必须做到这样的事情:

// Better delist from event - don't want our handler called from now on:
otherObject.TheEvent -= OnTheEvent;
// Good, now we can be certain that OnTheEvent will not run...
Run Code Online (Sandbox Code Playgroud)

实际的顺序可能是这种混合物:

// Copy the event delegate before checking/calling
EventHandler copy …
Run Code Online (Sandbox Code Playgroud)

c# events multithreading

230
推荐指数
6
解决办法
8万
查看次数

为什么C#不能自动提供对C++/CLI事件的线程安全访问?

EventHandler委托的MSDN文档:

与C#和Visual Basic示例相比,Visual C++示例代码不要求您创建线程安全的临时变量.Visual C++版本自动提供线程安全访问,使您可以直接引发事件.

为什么C#不能自动提供对C++/CLI事件的线程安全访问?

.net c# c++-cli

17
推荐指数
1
解决办法
925
查看次数

为什么标准C#事件调用模式是线程安全的,没有内存屏障或缓存失效?类似的代码怎么样?

在C#中,这是以线程安全方式调用事件的标准代码:

var handler = SomethingHappened;
if(handler != null)
    handler(this, e);
Run Code Online (Sandbox Code Playgroud)

其中,可能在另一个线程上,编译器生成的add方法用于Delegate.Combine创建新的多播委托实例,然后在编译器生成的字段上设置该实例(使用互锁比较交换).

(注意:出于这个问题的目的,我们不关心在事件订阅者中运行的代码.假设它在删除时是线程安全且健壮的.)


在我自己的代码中,我想按照以下方式做类似的事情:

var localFoo = this.memberFoo;
if(localFoo != null)
    localFoo.Bar(localFoo.baz);
Run Code Online (Sandbox Code Playgroud)

哪里this.memberFoo可以由另一个线程设置.(这只是一个线程,所以我不认为它需要联锁 - 但也许这里有副作用?)

(并且,显然,假设它Foo是"不可变的",我们在这个线程上使用它时不会主动修改它.)


现在我理解这是线程安全的明显原因:从引用字段读取是原子的.复制到本地可确保我们不会获得两个不同的值.(显然只能从.NET 2.0保证,但我认为它在任何理智的.NET实现中都是安全的吗?)


但我不明白的是:被引用的对象实例所占用的内存如何?特别是在缓存一致性方面?如果"writer"线程在一个CPU上执行此操作:

thing.memberFoo = new Foo(1234);
Run Code Online (Sandbox Code Playgroud)

什么保证Foo分配新内存的内存不会出现在"读取器"运行的CPU的缓存中,具有未初始化的值?什么确保localFoo.baz(上面)不读取垃圾?(跨平台的保证有多好?在Mono上?在ARM上?)

如果新创建的foo恰好来自游泳池呢?

thing.memberFoo = FooPool.Get().Reset(1234);
Run Code Online (Sandbox Code Playgroud)

从内存的角度来看,这似乎没有什么不同,只是一个新的分配 - 但是.NET分配器可能会让第一个案例有效吗?


在我提出这个问题时,我的想法是需要一个内存屏障来确保 - 不是因为读取依赖而不能移动内存访问 - 而是作为CPU的一个信号来清除任何缓存失效.

我的来源是维基百科,所以你要做的就是这样.

(我可能会猜测,也许在连动比较交换作家线程上无效缓存读者?或者,也许所有的读取原因失效吗?或者指针引用引起失效?我特别关注如何平台特有的这些东西的声音.)


更新:只是为了更明确地说明问题是关于CPU缓存失效以及.NET提供的保证(以及这些保证可能如何依赖于CPU架构):

  • 假设我们在字段Q(内存位置)中存储了引用.
  • 上CPU (作家)我们在初始化存储器位置的对象R …

c# memory-model thread-safety memory-barriers mesi

15
推荐指数
1
解决办法
667
查看次数

在C#中调用事件处理程序

我一直在努力学习如何在C#中使用事件处理程序,但我无法弄清楚以下代码中的处理程序(this,e):

public event EventHandler ThresholdReached;

protected virtual void OnThresholdReached(EventArgs e)
{
    EventHandler handler = ThresholdReached;
    if (handler != null)
    {
        handler(this, e);
    }
}
Run Code Online (Sandbox Code Playgroud)

它是否尝试使用事件(e)调用事件处理程序方法(this)?

c# events

5
推荐指数
1
解决办法
2万
查看次数