【问题标题】:Multithreaded Observer Pattern [closed]多线程观察者模式
【发布时间】:2012-02-03 13:42:00
【问题描述】:

我有一个问题,主题每次都会在不同的线程中更新。因此,无论何时更新主题,它都会相应地用新信息更新观察者。但是,如果观察者列表很长,则需要一些时间来更新所有观察者。想想一个经常更新的主题。当主体更新观察者时,“主体”对象被锁定,因此不能被不同的线程更新。这会为主题创建信息流量或导致信息丢失。

您知道如何在多线程环境中处理这些问题吗?另外,谁能推荐一些关于C++并行编程的书?

【问题讨论】:

  • 这是一个理论问题吗?因为您的问题可能有其他解决方案不涉及使用所谓的“模式”。
  • 如果主体持有的信息在观察者更新过程中没有变化或者变化没有任何后果,则不需要“锁定”。以只读文件为例。据我所知,如果我正确理解了您的观点,多线程将无法解决问题。其他线程也可以等到主题再次可用并且在超时后不返回,并且不会出现“信息丢失”,但这取决于您在描述中提供的信息量难以想象的上下文你的问题。

标签: c++ multithreading observer-pattern


【解决方案1】:

我用 Java 写了一个多线程观察者模式

import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

/**
 * An observer pattern that allows listeners to register(), unregister() in
 * multiple threads and also notify listeners in another thread.
 * 
 * A HashMap keeps track of the listeners and their status (active, obsolete).
 * When a listener unregister, its entry is marked as obsolete in this map.
 * 
 * During firing of an event, the observer notifies all the listeners that are
 * active, the active status will be stored in a Boolean that's synchronized so
 * rare race conditions like calling notify on an active listener that has just
 * turned obsolete will not happen.
 * 
 * 
 */
public class MultithreadedObserverPattern <T extends AbstractListener> {

    interface Handler<T> {
        void handleEvent(T listener);
    }

    class BooleanHolder {
        boolean val;

        BooleanHolder(boolean v) {
            val = v;
        }

        void set(boolean v) {
            val = v;
        }

        boolean get() {
            return val;
        }
    }

    Map<AbstractListener, BooleanHolder> listeners = new HashMap<AbstractListener, BooleanHolder>();

    public void register(AbstractListener l) {
        synchronized (listeners) {
            listeners.put(l, new BooleanHolder(true));
        }
    }

    public void unregister(AbstractListener l) {
        synchronized (listeners) {
            BooleanHolder status = listeners.get(l);
            if (status != null) {
                // notify call also syncing on status
                synchronized (status) {
                    status.set(false);
                }
            }
            // set to false
        }
    }

    public void notifyAll(Handler handler) {
        // here we do not synchroznie on listeners to avoid tricky lock situations
        // make a copy of the map
        List<Entry<AbstractListener, BooleanHolder>> activeListeners = new ArrayList<Entry<AbstractListener, BooleanHolder>>();
        List<AbstractListener> inactiveListeners = new ArrayList<AbstractListener>(); 

        synchronized (listeners) {
            for (Entry<AbstractListener, BooleanHolder> entry : listeners.entrySet()) {
                if (entry.getValue().get()) {
                    activeListeners.add(entry);
                } else {
                    inactiveListeners.add(entry.getKey());
                }
            }
        }
         // call the method on active listener
        // 
        for (Entry<AbstractListener, BooleanHolder> e : activeListeners) {
            BooleanHolder status = e.getValue();
            // remove those listeners that are no longer active
            synchronized (status) {
                if (status.get()) {
                      handler.handleEvent(e.getKey());
                }
            }
        }

        synchronized (listeners) {
            // remove inactive listeners
            for (AbstractListener l : inactiveListeners) {
                listeners.remove(l);
            }
        }
    }
}

【讨论】:

    【解决方案2】:

    考虑使用producer-consumer queues 或message queues。对于您的示例,您可以通过两种方式使用队列:

    1. 对主题的更改已排队。当有东西更新主题时,它会将新状态放入队列并立即返回。这样,在通知观察者时更新程序不会阻塞。您将需要一个线程来不断地使状态更改出列并更新观察者。

    2. 对观察者的通知已排队。每个观察者都有一个发布主题状态更改通知的队列。

    如果您使用的是 Qt 库,则可以使用带有 Qt::QueuedConnection 连接类型的信号和插槽机制。插槽通过接收者的事件队列并在接收者的线程中执行。这样,当接收者执行各自的槽时,发送者不会阻塞。

    您的程序可能很适合Actor model (paradigm)。以下是一些实现 Actor 模型的 C++ 库:

    您的程序也可能是Dataflow 范式的良好候选者。查看建议的Boost Dataflow 库,它支持threading。


    我没有要推荐的书,但请查看 Herb Sutter 关于 C++ 并发的 series of Dr Dobbs articles。

    【讨论】:

    • 顺便说一句:libcppa 支持演员的基于发布/订阅的组通信。主题可以很容易地建模为本地组。任意数量的线程/参与者(发布者)正在向组发送,任意数量的线程/参与者(接收者)只需加入组即可接收。
    猜你喜欢
    • 2013-03-05
    • 1970-01-01
    • 1970-01-01
    • 2013-12-02
    • 1970-01-01
    • 1970-01-01
    • 2015-12-31
    • 1970-01-01
    • 2023-03-30
    相关资源
    最近更新 更多