繁体   English   中英

取消订阅事件,并确保不会再次调用该处理程序

[英]Unsubscribing from events and being sure that the handler will not be called again

我有一个聊天会话类,它有一个MessageReceived事件,每当收到一条消息时都会引发该事件。 会话类还具有Disconnect事件,以关闭会话并防止进一步的消息被接收。

    public class Session
    {
        event EventHandler<string> MessageReceived;
        public void Disconnect();
    }

我订阅了Session的MessageReceived事件,并在我的处理程序中解码了该消息,然后使用它来更新应用程序-例如,一条消息可能包含有关特定用户的更新,或者有关用户加入的消息等。

当我想关闭我的应用程序时,我要做的第一件事是从MessageReceived事件中取消订阅,然后调用Disconnect。 然后,我要清理并处置该应用程序使用的所有资源。

现在,如果我不作任何考虑,我可能会认为,仅因为我已取消订阅MessageReceived并称为Disconnect,就不可能再引发任何MessageReceived事件。 显然不是这样。 在我取消订阅并调用Disconnect之前,事件处理程序已经启动的可能性还是很大的。

这里的问题是,在我调用Disconnect之后,我将继续清理和处置应用程序资源,但是这些都是假设可以在MessageReceived处理程序中访问的资源。 这是一个未处理的异常等待发生。

调用Disconnect之后,我想确定在继续进行拆卸之前不会再次调用事件处理程序。

请问SO可以告诉我这种情况下的最佳实践,并最好指出.NET框架中遇到和处理这种情况的示例。

谢谢

如果您担心竞争状况,请使用监视器(锁定)保护关键资源

线程同步

编辑 :您可以使用上面建议的标志,并用监视器保护标志:

object lockThis = new object();
bool isSubscribed = false;
...
lock(lockThis){
  publisher.SomeEvent += handler;
  isSubscribed = true;
}

... then in the handler

lock(lockThis){
   if (isSubscribed){
     // do stuff
   }
}

在必须处理来自多个源的刺激的系统中解决此问题的一种标准方法是将所有刺激及其参数集中到线程安全的队列中,然后由一个专用线程来处理这些项目。

从那里开始,关闭非常容易。 将“停止”事件加入队列,然后处理队列的线程很容易看到停止事件并停止查看队列。

这是Windows用于UI事件的模型。

因此,如果您拥有例如5个网络接口和一个线程,每个线程都接收数据包并希望确保处理事件的代理以安全的方式进行操作,那么这就是处理该方法的方式。 虽然这对于您需要的东西来说有些过大,但是它可以帮助您从更一般的角度考虑问题。

因此,这里有一个假设,假设您的事件处理程序仅从一个线程中触发,并且触发关闭的代码不必等待关闭完成; 如果没有,那就行不通了。 无论如何,要关闭,请手动触发该事件,告诉它该消息是伪造的关闭消息。 它将设置一个标志以指示它已关闭,然后注销事件处理程序,然后执行关闭调用。 假设事件处理程序仅在一个线程上发生,则可以确保仅尝试从该线程关闭内部状态。

如果可以从多个线程中触发该事件,那么它将不起作用-例如,如果UI线程是一个用于触发关机的线程,而套接字接收线程是一个用于触发实际事件的线程。 同样,如果由于其他原因您的关闭启动代码必须等待真正的关闭完成,则此操作将无法正常工作,因为它无法等待事件安全地发出信号,表明已完成。 如果您将所有真正的关机代码移至事件处理程序内部,则可能不是问题。

因此,这是另一种处理方式,稍微复杂一点:当您仍然处于事件处理程序中时,您不能让关闭代码继续执行; 并且如果您已经开始关闭,则无法输入事件处理程序。

您需要一些锁。

当您进入事件处理程序时,请抓住一把锁。 如果您获得了锁,并且发现您的关闭标志已设置,则只需返回即可。 否则,虽然仍处于锁定状态,但仍可以处理您的事件。

输入关机代码时,请抓住锁。 设置要关闭。 释放锁。 注销事件处理程序并进行清理。

如果您在运行关闭代码时正在处理事件,则关闭代码将锁定在锁上,直到事件完成。

当您在关闭代码中并保持锁定状态时,可能会发生事件。 事件代码将阻止等待锁定。 将关闭标志标记为已设置后,您将释放锁定,然后开始执行关闭操作。 因此,您将释放锁定,事件代码将唤醒,关机代码将继续运行,事件代码将看到设置了shutdown标志,并且它将退出而不接触任何资源。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM