我有一个问题,如何在多线程环境中正确添加/删除事件处理程序以进行异步回调。
我有MyCore类,它从ProxyDLL接收异步回调,该回调调度来自非托管代码的回调。我有订阅事件的表单(托管)。
附加/分离事件的正确方法是什么。我注意到多播委托已经_invocationcount了。它做什么?如果回调调用正在进行中,直到回调完成,事件的内在逻辑是否会阻止与事件分离?_invocationcount是为那个傀儡而存在的吗?从事件中扣留(一般来说)是踏步安全的吗?
class Form1
{
EventHandler m_OnResponse;
Int32 m_SomeValue;
Form1()
{
m_OnResponse = new EventHandler(OnResponseImpl);
m_MyCore.SetCallBackOnLogOn(m_OnResponse);
}
~Form1()
{
m_MyCore.ReleaseCallBackOnLogOn(m_OnResponse);
}
private OnResponseImpl(object sender, EventArgs e)
{
Thread.Sleep(60*1000);
m_SomeValue = 1; // <<-- How to/Who guarantees that Form1 obj is still
// alive. May be callback was invoked earlier and
// we just slept too long
if (!this.IsDisposed)
{
invokeOnFormThread(DoOnResponseImpl, sender, e);
}
}
}
class MyCore
{
private event EventHandler OnLogOn;
public void SetCallBackOnLogOn(EventHandler fn)
{
// lock (OnLogOn)
{
OnLogOn += fn;
}
}
ReleaseCallBackOnLogOn(EventHandler fn)
{
// lock (OnLogOn)
{
OnLogOn -= fn;
}
}
public void DoDispatchOnLogOn()
{
// lock (OnLogOn)
{
if (OnLogOn != null)
{
OnLogOn(this, null);
}
}
}
}
默认事件添加和删除操作已经是线程安全的。在大多数情况下,您无需担心该部分。您需要担心的是多播委托的调用。
public void DoDispatchOnLogOn()
{
EventHander local;
lock (this)
{
local = OnLogOn;
}
if (local != null)
{
local(this, null);
}
}
我在这里所做的是创建一个局部变量来保存OnLogOn
委托链。我在这里利用多播委托的不可变性,以便我们可以对 null 和调用序列进行线程安全检查。该lock
仅用于确保OnLogon
的"新鲜"读取,如果您不介意获得"过时"读取,则它是严格可选的。
更新:
我需要确保在以下情况下不会调用回调 ReleaseCallBackOnLogOn 结束了取消订阅委托。
在大多数情况下,我拥有的代码不会尝试执行任何已取消订阅的事件处理程序。在删除事件处理程序和引发事件之间存在着轻微的竞争,这可能导致事件处理程序被执行,即使它最近刚刚被删除。
我需要确保我的类实例仍然处于活动状态,直到调用 回调已完全完成。
委托保存对包含目标方法的类实例的引用。这将使您的实例保持根,因此不符合垃圾回收的条件。您无需担心这一点。
我应该指出,~Form1
终结器不是删除事件处理程序的好地方。请记住,委托持有对包含目标方法的实例的引用,因此在大多数情况下,不会调用终结器,也不会从事件中删除事件处理程序。