通知监听器内部或外部内部同步

发布于 2024-09-05 21:36:46 字数 813 浏览 5 评论 0原文

我正在为一个决定而挣扎。我正在编写一个线程安全库/API。可以注册侦听器,以便在发生有趣的事情时通知客户端。这两种实现中哪一种最常见?

class MyModule {
   protected Listener listener; 

   protected void somethingHappens() {
        synchronized(this) {
             ... do useful stuff ...
             listener.notify();
        }
    }
}

或者

class MyModule {
   protected Listener listener; 

   protected void somethingHappens() {
        Listener l = null;

        synchronized(this) {
             ... do useful stuff ...
             l = listener;
        }
        l.notify();
    }
}

在第一个实现中,侦听器在同步内收到通知。在第二个实现中,这是在同步之外完成的。

我觉得建议使用第二种,因为它可以减少潜在僵局的空间。但我很难说服自己。

第二种实现的缺点是客户端可能会收到“不正确”的通知,如果客户端在 l.notify() 语句之前访问并更改了模块,就会发生这种情况。例如,如果它要求模块停止发送通知,则无论如何都会发送该通知。第一次实现时情况并非如此。

多谢

I am struggling with a decision. I am writing a thread-safe library/API. Listeners can be registered, so the client is notified when something interesting happens. Which of the two implementations is most common?

class MyModule {
   protected Listener listener; 

   protected void somethingHappens() {
        synchronized(this) {
             ... do useful stuff ...
             listener.notify();
        }
    }
}

or

class MyModule {
   protected Listener listener; 

   protected void somethingHappens() {
        Listener l = null;

        synchronized(this) {
             ... do useful stuff ...
             l = listener;
        }
        l.notify();
    }
}

In the first implementation, the listener is notified inside the synchronization. In the second implementation, this is done outside the synchronization.

I feel that the second one is advised, as it makes less room for potential deadlocks. But I am having trouble to convince myself.

A downside of the second imlementation is that the client might receive 'incorrect' notifications, which happens if it accessed and changed the module prior to the l.notify() statement. For example, if it asked the module to stop sending notifications, this notificaiton is sent anyway. This is not the case in the first implementation.

thanks a lot

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

马蹄踏│碎落叶 2024-09-12 21:36:46

这取决于您在方法中获得侦听器的位置、您有多少个侦听器、侦听器如何订阅/取消订阅

假设从您的示例来看,您只有一个侦听器,那么您可能最好对不同部分使用关键部分(或监视器)类的而不是锁定整个对象。

您可以拥有一把锁,用于在方法内执行特定于当前对象/任务的任务,并拥有一把锁用于侦听器订阅/取消订阅/通知(即确保侦听器在通知期间不会更改)。

我还会使用 ReadWriteLock 保护您的侦听器引用(单个侦听器或侦听器列表)

回答您的评论:

我认为您应该在解锁类后通知侦听器。这是因为,该通知的结果可能会导致不同的线程尝试访问该类,但在某些情况下它可能无法做到这一点,从而导致死锁。

通知侦听器(如果像我所描述的那样受到保护)不应阻止任何需要该类功能的其他线程。最好的策略是创建特定于类状态的锁和特定于安全通知的锁。

如果您以挂起通知为例,这可能会被管理通知的锁所覆盖,因此,如果另一个线程“挂起”通知,则挂起将被处理或当前通知完成,如果另一个线程在正在处理任务并且发生通知,l.notify() 将不会发生。

Listener l = null;

synchronised(processLock_) {
   ... do stuff....
   synchronised(notifyLock_) {
      l = listener;
   }
} 
//
// current thread preempted by other thread that suspends notification here.
//

synchronised(notifyLock_) { // ideally use a readwritelock here...
   l = allowNotify_ ? l: null;
}
if(l)
   l.notify();

It depends on where you are getting listener in your method, how many listeners you have, how the listener subscribes/unsubscribes

Assuming from your example, you have only one listener then you might be better to use critical sections (or monitors) for different parts of the class rather than locking the entire object.

You could have one lock for performing tasks within the method that are specific to the object/task at hand, and one for the listener subscribe/unsubscribe/notify (that is to ensure that the listener is not changed during a notification).

I would also use a ReadWriteLock protecting you listener references (either single or list of listeners)

Answering you comment:

I think that you should notify the listener after you have unlocked the class. This is because, the result of that notification could result in a different thread trying to gain access to the class, which it may not be able to do, under certain circumstances, leading to deadlock.

Notifying a listener (if protected like I have described) should not hold up any other thread that requires the facilities of the class. The best strategy is to create locks that are specific to the state of the class and locks that are specific to safe notification.

If you take your example of suspending notifications, this could be covered by the lock that governs notifications, so if a different thread 'suspends' notifications, either the suspend will be processed or the current notification complete, if the other thread suspends notification between the task being processed and the notification happening, the l.notify() will not happen.

Listener l = null;

synchronised(processLock_) {
   ... do stuff....
   synchronised(notifyLock_) {
      l = listener;
   }
} 
//
// current thread preempted by other thread that suspends notification here.
//

synchronised(notifyLock_) { // ideally use a readwritelock here...
   l = allowNotify_ ? l: null;
}
if(l)
   l.notify();
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文