2016-08-05 88 views
0

我正在使用sigslot庫來觸發函數中的信號。該函數使用QtConcurrent :: run在一個線程中運行,並且信號在主線程中連接。 除了信號連接每次都不工作(假設大約有25%的故障),它的工作方式與預期相當。跨線程的sigslot信號

這種不穩定的行爲是有問題的,我找不到解決方案。取決於多線程上下文,sigslot庫中的信號具有不同的選項,但它們中沒有一個正在解決該問題。

之前努力提振,我真的想找到一個解決方案使用sigslot,因爲它是一個很簡單的圖書館,我只需要在這部分代碼基本使用的信號和槽保持。我不希望爲此使用Qt,因爲我寧願將代碼的相同部分留給Qt。

任何提示將不勝感激。

更新:由於某種原因,使用作爲絕望嘗試sigslot :: single_threaded似乎讓路更好的結果。

signal1<int, single_threaded> Sig1; 

我不是說它解決了問題,因爲它對我沒有意義。如文檔中所述: 單線程在單線程模式下,庫不會嘗試跨線程保護其內部數據結構 。因此,所有對構造函數,析構函數和信號 的調用都必須存在於單個線程中。

更新2:

這裏是一個MWE。但結果是相當隨機的。有時它完全有效,有時候不是全部。我知道這聽起來很奇怪,但這就是問題所在。我也嘗試了boost :: signals2而不是sigslot,但結果是完全相同的。有一個在升壓:: signals2一個可執行壞訪問::互斥鎖::()

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 
} 
+0

對於沒有製作MWE感到抱歉,我的確在尋找全局答案而不是代碼調試(就像「Qt和sigslot不會一起工作」)。由於行爲不穩定,上下文相當糾結,我認爲這不值得做一個例子 – brahmin

+0

最重要的缺失部分是:沒有「the」sigslot庫。這是一個廢棄的項目,有多個呃分解各種來源的階段。請添加您正在使用的版本的鏈接。 –

+0

那麼我不知道這個關於這個庫的。我開始使用,因爲我發現只有很好的反饋。我正在使用這個版本:https://sourceforge.net/p/sigslot/patches/3/ – brahmin

回答

2

薩拉·湯普森的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的。

+0

感謝您的回答。 Qt連接現在看起來沒問題。但是你是對的,我沒有做出任何具體的插槽是線程安全的。問題更多地涉及sigslot連接,該信號在Qt線程中觸發,但在主線程中(與sigslot插槽一起)連接。但也許我沒有正確理解你的答案。 – brahmin

+0

再說一遍,發生什麼事情並不重要,只要線程不同,那麼sigslot不支持它。仔細閱讀文檔:sigslot不知道(不能有任何!)關於各種物體在哪裏生活。 'QObject :: thread()'有很好的理由。沒有這種機制,線程安全信號槽系統就不可能實現。 –

+0

在MWE中使用boost並沒有給出更好的結果。你提出的使用QObject的例子是爲了替代sigslot或boost?我真的想要避免在QtConcurrent :: run中使用的計算庫中使用Qt,但是在沒有GUI的情況下使用庫時,這太重了。無論如何,這是非常奇怪的,現在在我的代碼中看起來工作正常(使用sigslot),但我不確定你說的是什麼。軟件應該是跨平臺等,我擔心將來會出現一些奇怪的行爲 – brahmin