2012-04-13 50 views
1

我有一個有趣的問題。使用Qt處理C++項目。跨平臺項目,但在Win上發展。如何使C函數有效地等待Qthread完成工作?

我有一個C風格的回調函數。它需要是C風格,我沒有選擇它。 在C風格的回調函數中完成的工作是重要且時間敏感的。因此,我有一些Qthread線程可以幫助解決工作負載。

我沒有使用Qt線程的運行方案,而是使用QThreads作爲解釋在QThread文檔的底部。 http://qt-project.org/doc/qt-5.0/qthread.html#wait

爲了清楚起見,我使用的QThread這樣:

QThread *thread = new QThread; 
Worker *worker = new Worker; 
worker->moveToThread(thread); 
thread->start(); 
QMetaObject::invokeMethod(worker, "doWork", Qt::QueuedConnection); 

的線程在應用程序的開始作出,QMetaObject :: invokeMethod中被饋送。

面臨的挑戰是,在QThread線程完成其工作之前,沒有C回調函數執行「任何事情」(以有效的方式)。我想讓回調函數以這樣一種方式等待,即它不會與工作線程競爭CPU(所以沒有繁忙的虛擬循環)。我也可以使用像sleep()這樣的東西,但這不是有效的,因爲如果線程「提前」完成,就會浪費睡眠。我想從工作人員發出一個信號,但問題是我的回調是一個C函數,所以我不明白它是如何捕捉Qt信號的。

+1

不能老是你包C函數的使用Qt類和捕獲信號有:

QueryThreadCycleTime(hThread, &start); QMetaObject::invokeMethod(&worker, "doWork", Qt::BlockingQueuedConnection); QueryThreadCycleTime(hThread, &stop); 

的基準測試結果如下圖呈現? – dvvrd 2012-04-13 18:16:53

+0

不,我不能。 C函數不僅僅是一個C函數,它是一個回調函數。一部分的lib。我不能改變它的性質,我只能執行它的身體。我對什麼/誰調用回調沒有影響力,我需要保持一個C回調函數。 – L123 2012-04-13 18:26:20

回答

0

好的,考慮這樣的問題。讓你的回調函數除了設置一些全局的標誌(或增加一些計數器)並且顯示你的函數是否被調用(或多少次)之外什麼也不做。然後從QObject繼承的imlement類(有槽)。您訂閱它以線程完成信號。當信號發出時,你只需調用另一個函數(用你的回調函數的邏輯),那麼它就被lib調用了很多次。它會起作用嗎?

+0

這聽起來像用錘子把一枚硬幣放入存錢罐 – 2012-04-13 18:47:27

+0

是的,它聽起來像這樣,但它滿足要求「我想使回調函數等待,以便它不會與CPU競爭工作者線程(所以不需要繁忙的虛擬循環)「。 – dvvrd 2012-04-13 18:55:21

+0

謝謝,但這是行不通的,因爲回調已經被外部源(操作系統)所調用...這是所有事情的催化劑,我仍然需要一種方法來阻止從完成和返回的回調函數工作完成之前。 – L123 2012-04-13 19:00:08

0

如果您有「牽手」線程,並且您完全確定該線程在完成工作後將自行退出。

http://qt-project.org/doc/qt-4.8/qthread.html#wait

但是!

如果你不想讓你的線程退出,只是要等到工人發出一些新葛你可能會做這樣的事情:

QEventLoop loop; 
connect(worker, SIGNAL(workFinished()), &loop, SLOT(quit())); 
QMetaObject::invokeMethod(worker, "doWork", Qt::QueuedConnection); 
loop.exec(); 
+0

是的,我不希望線程死掉,因爲新線程很昂貴。當你有超過1名工人時,這將如何工作?在我看來,其中一名工作人員完成後,事件循環終止。 – L123 2012-04-13 19:08:30

+0

然後,您需要爲自己創建一些「泳池」對象,這將會了解所有工作人員,並保持對他們的跟蹤。它可以使用QEventLoop aproach本身,只是檢查是否所有的工人完成 – 2012-04-14 05:19:27

0

問題是安靜了,但它想出了「相關問題」。由於這個問題很有趣,而且答案對Qt用戶來說很有用,所以我決定給出一個詳細的答案。

做上的功能等不同的QThread來完成,多個策略是可能的:

  1. 使用Qt::BlockingQueuedConnection
  2. 使用幼稚的while(anAtomicBoolean){}循環。
  3. 使用更巧妙的while(anAtomicBoolean){QThread::yieldCurrentThread();}循環。
  4. 使用QWaitCondition。使用QSemaphore

策略2和3看起來像不好的想法,因爲等待線程在等待檢查循環條件時必須處於活動狀態。然而,策略3具有釋放其他線程和進程的CPU時間的優勢。

其他3種策略的優點是在等待時讓線程休息,並且應該是相當的。然而,解決方案1和4不允許您(或者至少很容易)同時在不同線程中運行多個工作人員。所以最好的解決方案是使用信號量。

QSemaphore semaphore(2) 
worker1.sem = &semaphore; 
worker2.sem = &semaphore; 
semaphore.acquire(2); 
QMetaObject::invokeMethod(&worker1, "doWork", Qt::QueuedConnection); 
QMetaObject::invokeMethod(&worker2, "doWork", Qt::QueuedConnection); 
semaphore.acquire(2); // each worker release(1) when done 
semaphore.release(2); 

爲了證明我的觀點,我做了一個基準測試(如果有人感興趣,我會稍後提供代碼)。我使用QueryThreadCycleTime()來獲得等待線程消耗的週期數。就像這樣: Result graph