[英]Can events be used in a multi-threaded application safely
在下面的代码中,我有两个类,一个类在单独的线程中运行并触发事件,另一个类订阅此事件并从事件中接收数据。 我基于乔恩·斯凯特(Jon Skeet)的文章http://csharpindepth.com/Articles/Chapter2/Events.aspx建立的事件代码
在本文http://www.codeproject.com/Articles/37474/Threadsafe-Events中,它表示...
因此,我推荐与Jon Skeet最终在“代表和事件”末尾推荐的方法相同的方法:“不要那样做”,即,不要以多线程方式使用事件。 如果一个对象上存在一个事件,则只有一个线程应该能够订阅或取消订阅该事件,并且将引发该事件的线程是同一线程。
现在,显然我的设计打破了这一点,因为它在与订阅它不同的线程上触发了该事件。 我如何修改我的设计,使其遵循不以多线程方式使用事件的原则,或者这是不可能的?
我想到的另一种方法是将回调方法作为委托传递给B类,然后调用它而不是调用事件?
我可能杆子的末端完全错了,因此请您澄清一下。
注意:我知道.Net 4.0显然已经解决了这个问题,但是,如果在.Net 4之前有办法做到这一点,我仍然会感兴趣。
public delegate void MyDelegate(int a);
class A
{
void main()
{
B bObject = new B();
bObject.MyEvent += new MyDelegate(NiceMethod);
bObject.Run();
}
void NiceMethod(int a)
{
Console.Writeline({0}, a);
}
}
class B
{
readonly object eventLock = new object();
MyDelegate myDel;
public event MyDelegate MyEvent
{
add
{
lock (eventLock)
{
myDel += value;
}
}
remove
{
lock (eventLock)
{
myDel -= value;
}
}
}
//Assume this runs in a new thread and calls back data using MyEvent
//Have ommited thread code for simplicity
public void Run()
{
for (int i = 0; i < 100; i++)
{
Thread.Sleep(1000);
MyDelegate handler;
lock (someEventLock)
{
handler = myDel;
}
if (handler != null)
{
handler (i);
}
}
}
}
引发事件或侦听来自不同线程的事件没有任何问题。 侦听器负责处理从另一个线程中调用。 正如Marc Gravell在他的评论中指出的那样,编译器生成的add
和remove
实现支持(并且一直支持)在不同线程的事件之间进行侦听器的add
和remove
。 唯一的问题是以线程安全的方式引发事件,这可以通过使用生成的add
和remove
使用的相同类型的自旋锁来同步对事件的访问来完成:
class B
{
public event MyDelegate MyEvent;
protected OnMyEvent(int p_Arg)
{
// Delegates are immutable and add/remove default
// implementations always generate a new instance of the
// delegate. Therefore, tTmp (if not null) can be safely invoked
var tTmp =
System.Threading.Interlocked
.CompareExchange(ref MyEvent, null, null);
if (tTmp != null) {
tTmp(p_Arg);
}
}
//Assume this runs in a new thread and calls back data using MyEvent
//Have ommited thread code for simplicity
public void Run()
{
for (int i = 0; i < 100; i++)
{
OnMyEvent(i);
}
}
}
唯一可能发生的情况是,将侦听器从事件列表中删除后 ,将调用该侦听器。 恕我直言,侦听器必须能够处理这种情况,因为它处理从单独的线程调用的蜂鸣。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.