跨越线程的sigslot信号

时间:2016-08-05 13:07:51

标签: c++ multithreading qt signals-slots

我使用sigslot库来触发函数中的信号。此函数在使用QtConcurrent :: run的线程中运行,并且信号在主线程中连接。 它按预期工作得很好,除了信号连接每次都不起作用(让我们说大约25%的失败)。

这种不稳定的行为是有问题的,我无法找到解决方案。 sigslot库中的信号根据多线程上下文有不同的选项,但没有一个能解决问题。

在尝试提升之前,我真的想找到一个继续使用sigslot的解决方案,因为它是一个非常简单的库,我只需要在这部分代码中基本使用信号和插槽。而且我不想使用Qt,因为我更喜欢将这部分代码留给Qt。

任何提示都会非常感激。

更新:由于某种原因,使用sigslot :: single_threaded作为绝望的尝试似乎让位给了更好的结果。

signal1<int, single_threaded> Sig1;

我并不是说它解决了这个问题,因为它对我没有意义。如文档中所述: Single Threaded在单线程模式下,库不会尝试保护其内部数据结构 跨线程。因此,必须对构造函数,析构函数和信号进行所有调用 必须存在于一个线程中。

更新2:

这是一个MWE。但结果很随意。有时它完全有效,有时不是全部。我知道这听起来很奇怪,但这就是问题所在。我也试过boost :: signals2而不是sigslot,但结果却完全相同。在boost :: signals2 :: mutex :: lock()

中存在可执行的错误访问
class A {
    public :
    A() {}
    ~A() {}
    sigslot::signal1<int, sigslot::multi_threaded_local> sigslot_signal;
    boost::signals2::signal<void (int)> boost_signal;
    void func_sigslot() {
        for (int i=0;i<4;i++) {
            sigslot_signal.emit_signal(i);
        }
    }
    void func_boost() {
        for (int i=0;i<4;i++) {
            boost_signal(i);
        }
    }
};

class B : public sigslot::has_slots<sigslot::multi_threaded_local> {
    public :
    B() {}
    ~B() {}
    void test(int i) {
        std::cout << "signal triggered, i=" << i << std::endl;
    }
};

void main() {
    A a;
    B b;
    a.sigslot_signal.connect_slot(&b, &B::test);
    a.boost_signal.connect(boost::bind(&B::test, &b, _1));
    QtConcurrent::run(&a, &A::func_sigslot);//->crashes when signal emitted
    QtConcurrent::run(&a, &A::func_boost);//->crashes when signal emitted

    boost::thread t1(boost::bind(&A::func, &a));
    t1.join();//works fine
}

1 个答案:

答案 0 :(得分:3)

Sarah Thompson的sigslot图书馆(如果你使用的话)是陈旧的,不受支持的,而且似乎非常错误。没有任何类型的测试工具。原始资源不在现代编译器下编译。由于MSVC之前将模板处理为令牌列表,因此存在隐藏的拼写错误:显然部分代码从未使用过!

我强烈建议您只使用Qt或其他信号槽库。

唉,你的方法不起作用:sigslot library不知道Qt的线程上下文,也没有与Qt的事件循环集成。从错误的线程上下文调用插槽。既然你可能没有把你的插槽写成线程安全的,那么他们就不会做正确的事情而且看起来不会工作。

sigslot库的线程支持仅保护库自己的数据,而不保护您的数据。设置多线程策略仅影响库的数据。这与Qt形成鲜明对比,其中每个QObject的线程上下文都是已知的,并使信号槽系统能够安全地行动。

为了使其正常工作,您需要在您正在调用其插槽的所有QObject中公开一个线程安全的界面。这可以很简单:

class Class : public QObject {
  Q_OBJECT
public:
  Class() {
    // This could be automated via QMetaObject and connect overload
    // taking QMetaMethod
    connect(this, &Class::t_slot, this, &Class::slot);
  }
  Q_SIGNAL void t_slot();
  Q_SLOT slot() { ... }
}

不是连接到slot(),而是连接到t_slot(),其中t_前缀代表线程安全/ thunk。