2013-08-30 45 views
2

我正在使用QtConcurrent做一些沉重的背景圖像處理,我想顯示圖像,而其中的一部分正在逐步更新。 圖像的每一行分別計算並傳遞一個函子。Qt併發映射和進度報告

爲了計算整個圖像然後我有項的序列,我傳遞給QtConcurrent映射,並且每個線發射的信號時,它完成計算

下面是類工人的實例化:

//living in the main(gui) thread ! 
    Worker::Worker(VideoEngine* engine):_engine(engine){ 
     _watcher = new QFutureWatcher<bool>; 
     _watcher->setPendingResultsLimit(200); 
     connect(_watcher, SIGNAL(resultReadyAt(int)), this, SLOT(onProgressUpdate(int))); 
     connect(_watcher, SIGNAL(finished()), engine, SLOT(engineLoop())); 
    } 

下面是報告進度插槽:

void Worker::onProgressUpdate(int i){ 
    if(i < (int)_rows.size() && i%10==0){ 
     cout << " index = " << i << " y = "<< _rows[i] << endl; 
     _engine->checkAndDisplayProgress(_rows[i],i); 
    } 
} 

現在用法:

void Worker::_computeTreeForFrame(.../*unrelevant args*/){ 
.... 
.... 
    _watcher->setFuture(
        QtConcurrent::mapped(_sequence, 
        boost::bind(&VideoEngine::metaEnginePerRow,_1,output))); 
    } 
} 

所有的信號都被髮射出去,但是隻有當Qtconcurrent :: mapped與序列中的所有項目完成時纔會調用onProgressUpdate插槽。

當執行它時,序列正在處理時有很大的延遲,然後所有的插槽都會在後面順序執行。

我試過所有類型的信號/插槽連接,並沒有一個改變了這種行爲。

任何線索?

++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++++++++++++ 編輯後shf建議 +++++ ++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++

此調用是在主(gui)線程中完成的。 我改變了呼籲:

_computeFrameWatcher->setFuture(QtConcurrent::run(_worker,&Worker::computeTreeForFrame)); 

由於_computeTreeForFrame現在在另一個線程中執行的,我改變了調用QtConcurrent ::映射到:

_watcher->setFuture(QtConcurrent::mapped(_sequence, 
        boost::bind(&VideoEngine::metaEnginePerRow,_1,output))); 
_watcher->waitForFinished(); 

這將導致完全行爲之前相同。

++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++++++++++++馬立克ř建議 後 EDIT ++++ ++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++

好吧,所以我做了這樣的測試,這裏是我觀察到的:

QtConcurrent ::地圖:

  • 不發出信號resultReadyAt(int)

QtConcurrent ::映射

  • 發出resultReadyAt(int)完成

如果對地圖功能的調用在一個單獨的線程完成相同的步驟不要緊,只有當行爲遭遇。

我也給一試的信號progressValueChanged(int)作爲Qt的progressDialog例子說明。 信號progressValueChanged(int)得到僅射出的2行在圖像中(第一個和最後)。 這真的很奇怪,因爲在Qt進度對話框中它會順利發送。

我改了一下Qt的例子推出地圖功能在另一個線程比主線程,它仍然運作良好在這種情況下。

的問題,必須從其他地方出現。

也許GUI事件循環做一些我不奢望?我不知道什麼。

我現在將嘗試QtConcurrent :: mappedReduced並與結果:-)

報告++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++給人一種嘗試QtConcurrent後 編輯:: mappedReduced +++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++

它不工作,並呼籲只有當「地圖」功能完成的「降低」功能。換句話說,它與之前的信號/插槽機制相同。

我低的可能性現在

運行++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++++++++++ + 編輯我回來了一個解決方案儘可能接近Qt的進度對話框例如 +++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++++++++++ ++++如果我不能得到比Qt的例子相同的行爲

一定出事了。

下面是現在的代碼:

//created in the main thread! (gui) 
Worker::Worker(VideoEngine* engine):_engine(engine),_watcher(0){ 
    _watcher = new QFutureWatcher<void>; 
    _watcher->setPendingResultsLimit(200); 
    connect(_watcher,SIGNAL(progressValueChanged(int)), _engine, 
        SLOT(onProgressUpdate(int))); 
    connect(_watcher, SIGNAL(finished()), engine, SLOT(engineLoop())); 

} 

//executed on the main thread 
void Worker::computeTreeForFrame(...){ 
... 
_watcher->setFuture(QtConcurrent::map(_sequence,boost::bind(metaEnginePerRow,_1,output))); 
... 
} 

到computeTreeForFrame的電話...

... 
    _worker->computeTreeForFrame(); 
... 

此通話插槽完成

它發出線0和最後一行的信號,如前所述,但不發出任何其他信號。

不應該這樣做是完全的Qt例子嗎?

+0

您的問題描述不完整。這很難幫助,但我會嘗試。 –

+0

怎麼這樣?你可以說得更詳細點嗎 ?你想要什麼信息? – Lex

+0

編輯答案。 – Shf

回答

0

從任務描述看起來你應該使用mappedReduced。問題是我沒有看到獲得部分結果的好方法。克服這個問題的一種方法是發出信號形式的減少功能。

this thread可能有所幫助。

0

看來,QtConcurrent::mapped不會將VideoEngine :: metaEnginePerRow放在另一個線程中,由文檔判斷。如果圖像是在與GUI相同的線程中處理的,那麼無論您選擇哪種類型的連接,您的插槽都將在處理後執行,就像您所描述的那樣。

的解決方案是運行Worker::_computeTreeForFrame在另一個線程(我的理解,你的主要處理功能)經由QtConcurrent::run或把你Worker對象在另一個線程可能經由QObject::moveToThread()。然後,你應該使用的連接類型是Qt::QueuedConnection(或者如果你在連接之前將Worker置於另一個線程中,你甚至可以使用Qt :: AutoConnection連接or Qt::UniqueConnection,調用者和接收者將會在不同的線程中,所以qt會自動選擇QueuedConnection `)

編輯:

我不知道,但你的_watcher = new QFutureWatcher<bool>;仍然在主線程創建的,如果你打電話

_watcher->setFuture(QtConcurrent::mapped(_sequence, 
       boost::bind(&VideoEngine::metaEnginePerRow,_1,output))); 
_watcher->waitForFinished(); 

_watcher一套GUI線程等待,在哪呢被創建或線程,執行該命令的位置。如果_watcher->setFuture(QtConcurrent::mapped(_sequence, boost::bind(&VideoEngine::metaEnginePerRow,_1,output)));如果一個函數結束,是否需要_watcher->waitForFinished();? Qt在執行後會立即銷燬線程,並設置處理函數運行,爲什麼要等待?

_computeFrameWatcher應該是QFuture<void*>類型。

EDIT2:

好吧,之前我放棄了,我建議你測試QObject::moveToThread

你叫_worker->computeTreeForFrame();之前,把它放在另一個線程:

QThread *workerThread=new QThread(); 
_worker->moveToThread(); 
_worker->computeTreeForFrame(); 
/* connect _worker's finished signal with workerThread::quit and deleteLater slots */ 

並在所有連接_worker應該是DirectConnection,並且_worker和主(GUI)線程之間的所有連接都應該與QueuedConnection連接。也可以在_worker構造函數中創建新線程並立即將其移動到另一個線程,這樣可以在_worker的析構函數中銷燬線程,並且不用擔心GUI線程中的線程問題

+0

我試圖在computeTreeForFrame中創建_watcher,因此它屬於QtConcurrent :: run的線程,但信號從未發出,但它們都不是 – Lex

+0

1)將_watcher放在單獨的線程中使其不發出任何信號。 2)將它與GUI線程作爲GUI線程的同一線程,但在另一個線程中執行map函數使其發出1-2個progressValueChanged信號。 3)將它與GUI線程放在同一個線程中,並在GUI線程中執行map函數產生與2)完全相同的行爲。 – Lex

+0

@Lex也許QConcurrentRun是不夠的,如果QThread不會幫助,我不知道問題出在哪裏。現在它聽起來就像通常的「主線程中的繁重處理」 - 「gui凍結直到處理完成」。它典型的症狀是,所有的計算都必須在工作線程中進行,並且所有繪製都在GUI線程中進行。 – Shf