Qt中實現QThread線程同步QFtp
在Qt中實現QThread線程同步QFtp ,對于QFtp,它是一個用來實現FTP協議的類,詳情查閱資料。接觸Qt沒有多長時間,但簡單幾個小例子已經讓我感受到Qt在C++運用方面的強大。寫了一個小程序,需要在一個單獨的線程中使用QFtp來獲取FTP服務器上面的文件。FTP是FileZilla。
有兩個問題我比較關心:
(1)QThread到底如何使用
(2)QFtp是Async(異步)操作,也就是說例如connectToHost這樣的函數都是立刻返回,當操作完成后QFtp會發出signal。然而既然我的Ftp操作是在一個單獨的線程,我想寫一個函數downloadFtpFile() 來完成從connect到login到下載文件等一系列的操作,然后再返回。相當于我需要Sync(同步)的操作,所以需要等待(block)每個Ftp命令的結果。
在該文章最后有一個推薦的使用QThread的方法。我在這里想補充一點:obj.moveToThread(&thread); 這句話將obj從主線程移動到了thread對象所在的線程。但如果obj的對象里面有其他的變量,那么這些變量是在主線程中生成的。所以如果這些變量中有類變量,不能將obj的this指針作為parent傳給他們。
對于第二個問題,我使用了QSemaphore類來完成我的block和同步操作:在slot函數里面接收QFtp命令執行結果的signal,釋放信號,同時downloadFtpFile()函數里在調用完每一個QFtp異步命令后等待信號。在有點令人失望的是QSemaphore在通過tryAcquire()等待信號的時候是不處理事件event的。但是我需要在等待的時候程序也能觸發slot,告訴我當前命令的執行情況。所以我使用了一個小循環,里面調用qApp->processEvents();來讓我的slot函數被觸發。下面是代碼例子(只是樣例,并不完全符合C++語法):
首先是我的下載Ftp文件的函數:
- downloadFtpFile () //該函數在單獨線程里執行
- {
- int m_idFtpOp; // 該變量用來存放每一個QFtp命令ID
- int nVal;
- QFtp*pFtp=newQFtp (this); // 生成QFtp工具對象
- connect (pFtp,SIGNAL(listInfo(QUrlInfo)),this,SLOT(slotFtpListInfo(QUrlInfo))); // 我們需要listinfo,因為我們需要下載ftp所有當前目錄文件
- connect (pFtp,SIGNAL(commandFinished(int,bool)),this,SLOT(slotFtpCmdFinished(int,bool)));
- // 每個QFtp命令完成之后,會發出commandFinished信號,我們在槽函數中處理該信號
- m_idFtpOp = pFtp->connectToHost (<FTP地址>, 21); // 連接到遠程FTP Server
- bRet=false;
- nVal=100;
- while (bRet == false) // 使用nVal變量來做一個10000ms(10s)的超時
- {
- nVal--;
- if (nVal == 0)
- break;
- qApp->processEvents(); // 這里每100ms處理一次event,使slot函數能夠被調用
- bRet=m_SemOp.tryAcquire (1,100); // 等待信號100ms
- }
- if (!bRet || m_bFtpOpError) // 如果超時,或者slot函數中將m_bFtpOpError置成true,則關閉Ftp,返回錯誤
- {
- pFtp->abort();
- pFtp->deleteLater();
- return ERRCODE_FCC_FTP_CONN_TIMEOUT;
- }
- }
下面是槽函數
- slotFtpCmdFinished (int id, bool error)
- {
- if (m_idFtpOp == id) // 如果返回的id是當前正在操作的命令
- {
- if (error)
- m_bFtpOpError=true;
- else
- m_bFtpOpError=false;
- m_SemOp.release(); // 釋放信號(使downloadFtpFile函數中m_SemOp.tryAcquire()返回true)
- }
- }
以上的代碼只演示了對QFtp第一個命令connectToHost的等待過程。下面的login,list,get等操作都使用這個方法。
注意:在此例中,QFtp是在當前線程生成的,所以信號listInfo(QUrlInfo)的connect方式是direct連接。如果QFtp是在另一個線程生成(比如說是在函數downloadFtpFile所在類的構造函數中),那么第一:不能將this指針作為parent傳給QFtp對象,第二:需要使用qRegisterMetaType<QUrlInfo>("QUrlInfo");來注冊QUrlInfo類,因為信號發射與接收在不通的線程中,信號使用queued的方式。如果不注冊QURlInfo類,會在運行時動態報告錯誤。
總結:本文介紹的是在Qt中如何實現QThread線程同步QFtp ,看過本文之后,如果對于QThread不了解的話,那么請參考Qt中QThread使用方法這篇文章。使用本文介紹的方法,可以在獨立的線程中用同步的方式使用QFtp。在某些場合,尤其是采用應答機制的系統中,這樣的實現可以很大程度上簡化程序流程。
【編輯推薦】