考慮這樣一種需求,使用Qt的線程類QThread在後臺執行操做(好比說拷貝文件)的時候發生了錯誤,產生了一個錯誤信息須要提醒給用戶,在後臺輸出很顯然是不夠的,由於用戶可能根據就沒有任何控制檯可供程序輸出信息。多線程
這是本人本身作得一個仿Win10文件拷貝對話框的一個文件拷貝對話框app
該問題糾結到根本是由於Qt的任何窗口代碼都必須在主線程(也就是main函數所在的那個線程)中執行。若是在後臺發生錯誤須要出對話框提示給用戶的話,必須可以將後臺信息阻塞性的發送給前臺,在前臺圖形類的程序執行完畢後再返回。函數
那麼在Qt中先後臺如何通訊呢,因爲QThread是繼承自QObject的,很天然你們會想到使用信號槽來鏈接:oop
bool QObject::connect ( const QObject * sender, const char * signal, const QObject * receiver, const char * method, Qt::ConnectionType type = Qt::AutoConnection )this
相信你們對於以上connect函數的定義是很是熟悉了,關鍵在於最後一個參數「Qt::ConnectionType」,如下是該參數的詳細說明:spa
Qt::DirectConnection
When emitted, the signal is immediately delivered to the slot.
假設當前有4個slot鏈接到QPushButton::clicked(bool),當按鈕被按下時,QT就把這4個slot按鏈接的時間順序調用一遍。顯然這種方式不能跨線程(傳遞消息)。.net
Qt::QueuedConnection
When emitted, the signal is queued until the event loop is able to deliver it to the slot.
假設當前有4個slot鏈接到QPushButton::clicked(bool),當按鈕被按下時,QT就把這個signal包裝成一個 QEvent,放到消息隊列裏。QApplication::exec()或者線程的QThread::exec()會從消息隊列裏取消息,而後調用 signal關聯的幾個slot。這種方式既能夠在線程內傳遞消息,也能夠跨線程傳遞消息。線程
Qt::BlockingQueuedConnection
Same as QueuedConnection, except that the current thread blocks until the slot has been delivered. This connection type should only be used for receivers in a different thread. Note that misuse of this type can lead to dead locks in your application.
與Qt::QueuedConnection相似,可是會阻塞等到關聯的slot都被執行。這裏出現了阻塞這個詞,說明它是專門用來多線程間傳遞消息的。指針
Qt::AutoConnection
If the signal is emitted from the thread in which the receiving object lives, the slot is invoked directly, as with Qt::DirectConnection; otherwise the signal is queued, as with Qt::QueuedConnection.
這種鏈接類型根據signal和slot是否在同一個線程裏自動選擇Qt::DirectConnection或Qt::QueuedConnectioncode
很顯然在不一樣的線程間發送信號還但願發送信號的一端必須阻塞性的等待槽函數返回,那麼「Qt::BlockingQueuedConnection」是咱們的不二之選。
鏈接方式有了,而後是數據共享的問題,試想「後臺線程中的數據如何可以被前臺所使用,並且前臺後臺不必定在一個類裏面?把數據打包經過信號傳給前臺?」想一想就是很麻煩的事情,難道每一個這樣的需求場合都要作一遍這樣的事情嗎?感謝時間,由於時間穿過2011年,C++的新標準已經完美的解決了這個問題,那就是函數對象。
Qt的4.8.6版本所使用的mingw4.9.2版本是支持C++11的,若是你用的是老掉牙的rhel5系統,則須要升級編譯器了,由於C++11要在GCC 4.5以上的版本中才會支持。
首先咱們定義一個類:FunctionTransfer(函數大挪移),這個類繼承自QObject,並使用Q_OBJECT標籤來使用信號槽機制。代碼中的「std::tr1::function<void()>」就是C++標準庫中大名鼎鼎的函數對象。
class FunctionTransfer : public QObject
{
Q_OBJECT
public:
///@brief 構造函數
explicit FunctionTransfer(QObject *parent = 0);
public:
///@brief 制定函數f在main中執行
static void execinmain(std::tr1::function<void()> f);
signals:
///@brief 在別的線程有函數對象傳來
void comming(std::tr1::function<void()> f);
public slots:
///@brief 執行函數對象
void exec(std::tr1::function<void()> f);
};
而後是源文件:
//在全局數據區實例化一個FunctionTransfer的實例,該實例所在的縣城就是主線程。
FunctionTransfer main_thread_forward;
void FunctionTransfer::execinmain(std::tr1::function<void()> f)
{
main_thread_forward.exec(f);
}
FunctionTransfer::FunctionTransfer(QObject *parent) :
QObject(parent)
{
connect(this,SIGNAL(comming(std::tr1::function<void()>)),this,SLOT(exec(std::tr1::function<void()>)),Qt::BlockingQueuedConnection);
}
void FunctionTransfer::exec(std::tr1::function<void()> f)
{
if(Gt::isMainThread())
{
f();
}
else
{
emit this->comming(f);
}
}
很是簡單的邏輯,若是在主線程就執行,若是不是在主線程就發給主線程,主線程接到以後就執行。
類有了,接下來考慮實用的場合,好比有一個類 A,A有個方法f不能再後臺執行,須要跑到前臺,怎麼辦呢,上代碼:
做爲參數的lamda表達式捕獲了類A的this指針,而後轉換爲C++的函數對象,而後跑到前臺去執行了,執行完成後纔會返回,是否是灰常簡潔。
FunctionTransfer::execinmain([this](){this->f();});