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