0

我有一个聊天会话类,它有一个 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框架中遇到和处理这种情况的示例。

谢谢

4

2 回答 2

0

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

线程同步

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

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

... then in the handler

lock(lockThis){
   if (isSubscribed){
     // do stuff
   }
}
于 2013-03-30T03:10:56.757 回答
-1

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

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

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

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

所以这里有一个想法,假设您的事件处理程序只从一个线程中触发,并且触发关闭的代码不必等待关闭完成;如果没有,这将行不通。无论如何,要关闭,手动触发事件,告诉它消息是假的关闭消息。它将设置一个标志以指示它已关闭,然后取消注册事件处理程序,然后执行关闭调用。假设事件处理程序只发生在一个线程上,您将保证您只会尝试关闭该线程的内部状态。

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

所以这里有另一种处理方式,它有点复杂:当你还在事件处理程序中时,你不能让你的关闭代码继续运行;如果您已经开始关闭,则无法输入事件处理程序。

你需要一些锁。

当你进入你的事件处理程序时,抓住一把锁。如果您获得了锁并发现您的关闭标志已设置,那么就返回。否则,在仍处于锁定状态时,处理您的事件。

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

如果您在关闭代码运行时正在处理一个事件,那么关闭代码​​将在锁上阻塞,直到事件完成。

当您处于关闭代码中并持有锁时,可能会发生事件。事件代码将阻塞等待锁。在将关闭标志标记为已设置后,您将释放锁定,然后开始关闭。因此,您将释放锁定,事件代码将唤醒,关闭代码将继续运行,事件代码将看到已设置关闭标志,并且它将退出而不触及任何资源。

于 2013-03-30T03:45:31.867 回答