使用Glib::Dispatcher

连接到sigc::signal对象的槽将会在调用信号emit()operator()()方法的线程被调用。而Glib::Dispatcher的行为与此相反:与其连接的槽将在构造Glib::Dispatcher对象的线程执行(该线程必须正在运行Glib主循环)。如果在GUI主线程上构造了Glib::Dispatcher,任何工作线程都可以在其上调用emit()并让槽安全的执行连接的gtkmm函数。

一些线程安全规则对于Glib::Dispatcher依旧适用。如前所述,必须在接收器线程(将在该线程的主循环中执行连接的槽)构造Glib::Dispatcher对象。默认情况下接收器线程应该是程序的主进程,尽管Glib::Dispatcher有一个可以接受Glib::MainContext对象为参数并且可以在任何运行Glib主循环的线程中调用的构造函数。只有接受者线程才应该在Glib::Dispatcher对象上调用connect()或操纵相关的sigc::connection对象,除非你为此提供了额外的同步措施。一旦接收器线程连接了槽,任何工作现场都可以不加锁直接在Glib::Dispatcher对象上调用emit(),但前提是该对象是在工作线程启动前构造的,如果不是则需要进行额外的同步以确保可见性。

除了连接的槽在接收者线程执行以外,Glib::Dispatcher对象的行为与sigc::signal<void()>对象十分相似。因此它们不能传递未绑定的参数,也不能返回值。要传递未绑定参数最好的方法是使用线程安全的(同步)队列。尽管大多数编写多线程代码的人都会有一个可用的多线程代码,但是编写glibmm时没有(尽管线程安全与强异常安全结合有些微妙,但编写它们相对容易)。

接收者线程和工作线程都可以调用Glib::Dispatcheremit()方法,虽然此操作需要在合理的范围内进行。在类Unix系统上Glib::Dispatcher共享同一个公共管道,从理论上讲在一个负载很大的系统上运行使用很大数量Dispatcher对象的程序至少可以填满系统。如果在接收者线程的主循环有机会读取并清空管道之前,管道已填满且接收器线程在此时尝试对Dispatcher调用emit()并因此对管道进行写入,这样会导致死锁。当然在接收者线程想要发出信号可以使用普通的sigc::signal<void()>对象。