在任何一門語言中,多線程都是一個(gè)相對其他方面比較重要的點(diǎn),這里面的知識體系很龐大,同步和異步之間的處理方式,以及IO多路復(fù)用等等各種進(jìn)行性能優(yōu)化的方面,在往上層一點(diǎn)我們不可能一直進(jìn)行系統(tǒng)層次的調(diào)用,這樣太費(fèi)時(shí)間也太麻煩,就到設(shè)計(jì)模式這里,比如反應(yīng)器(Reactor)模式,再者多線程對代碼的敏感程度較高,很對細(xì)微的改變可能會(huì)帶來意向不到的效果,這更要求我們對于我們寫的代碼有更深次的理解,不僅僅是代碼本身,還要求代碼執(zhí)行階段鎖遇到的各種問題,這就非??简?yàn)一個(gè)程序員的功底。
1.多線程及簡單實(shí)例
QT5的多線程可以使用QtConcurrent和QThread兩種方式來實(shí)現(xiàn)。
- 使用QtConcurrent: QtConcurrent提供了一種簡單的方式來編寫并行的代碼。通過使用QtConcurrent::run()函數(shù),可以在后臺(tái)線程中執(zhí)行一個(gè)函數(shù)。
#include <QtConcurrent>
#include <QDebug>
void myFunction()
{
qDebug() << "Running in background thread";
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QtConcurrent::run(myFunction);
qDebug() << "Running in main thread";
return a.exec();
}
- 使用QThread: QThread類提供了一個(gè)線程對象,可以通過繼承QThread來實(shí)現(xiàn)自定義的線程類。
#include <QThread>
#include <QDebug>
class MyThread : public QThread
{
public:
void run() override
{
qDebug() << "Running in background thread";
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
MyThread thread;
thread.start();
qDebug() << "Running in main thread";
return a.exec();
}
以上示例中,myFunction()函數(shù)或者M(jìn)yThread類的run()函數(shù)會(huì)在后臺(tái)線程中執(zhí)行,而主線程會(huì)繼續(xù)執(zhí)行下面的代碼。
需要注意的是,當(dāng)使用QThread時(shí),不要直接調(diào)用run()函數(shù),而是通過start()函數(shù)來啟動(dòng)線程。另外,在多線程編程中,需要注意線程安全性和共享資源的競爭問題。
2.多線程控制
2.1互斥量
在QT中,可以使用QMutex(互斥量)來控制多個(gè)線程對共享資源進(jìn)行互斥訪問,以避免競爭條件和數(shù)據(jù)損壞。
下面是一個(gè)使用QMutex的簡單示例:
#include <QCoreApplication>
#include <QThread>
#include <QDebug>
#include <QMutex>
QMutex mutex;
int sharedData = 0;
class MyThread : public QThread
{
public:
void run() override
{
mutex.lock();
// 訪問共享資源
for (int i = 0; i < 10000; i++) {
sharedData++;
}
mutex.unlock();
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
MyThread thread1;
MyThread thread2;
thread1.start();
thread2.start();
thread1.wait();
thread2.wait();
qDebug() << "sharedData: " << sharedData;
return a.exec();
}
在這個(gè)示例中,我們創(chuàng)建了兩個(gè)線程來遞增sharedData
變量的值。為了確保對sharedData
的訪問是互斥的,我們使用了mutex
對象。在run()
函數(shù)中,我們使用mutex.lock()
來鎖定互斥量,然后執(zhí)行對共享資源的訪問,最后使用mutex.unlock()
來解鎖互斥量。
這樣就保證了同時(shí)只有一個(gè)線程可以訪問共享資源,避免了數(shù)據(jù)損壞。
2.2信號量
在QT中,可以使用QSemaphore(信號量)來控制多個(gè)線程對資源的訪問。信號量允許指定一個(gè)資源的數(shù)量,線程可以通過獲取和釋放信號量來控制對資源的訪問。
下面是一個(gè)使用QSemaphore的簡單示例:
#include <QCoreApplication>
#include <QThread>
#include <QDebug>
#include <QSemaphore>
QSemaphore semaphore(1); // 初始化信號量為1
int sharedData = 0;
class MyThread : public QThread
{
public:
void run() override
{
semaphore.acquire(); // 等待獲取信號量
// 訪問共享資源
for (int i = 0; i < 10000; i++) {
sharedData++;
}
semaphore.release(); // 釋放信號量
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
MyThread thread1;
MyThread thread2;
thread1.start();
thread2.start();
thread1.wait();
thread2.wait();
qDebug() << "sharedData: " << sharedData;
return a.exec();
}
在這個(gè)示例中,我們創(chuàng)建了兩個(gè)線程來遞增sharedData
變量的值。為了確保對sharedData
的訪問是互斥的,我們使用了一個(gè)信號量semaphore
,并將其初始化為1。
在每個(gè)線程的run()
函數(shù)中,我們首先使用semaphore.acquire()
來等待獲取信號量。一旦線程獲取到信號量,它就可以訪問共享資源,進(jìn)行對sharedData
的遞增操作。最后,線程使用semaphore.release()
來釋放信號量。
由于我們將信號量初始化為1,所以只有一個(gè)線程能夠獲取到信號量,而另一個(gè)線程必須等待。這樣就確保了對共享資源的訪問是互斥的。
2.3線程等待及喚醒
在Qt中,可以使用QWaitCondition類來控制線程的等待和喚醒。QWaitCondition提供了一個(gè)條件變量,可以讓線程在某個(gè)條件滿足時(shí)等待,并在條件滿足時(shí)被喚醒。
下面是一個(gè)使用QWaitCondition的簡單示例:
#include <QCoreApplication>
#include <QThread>
#include <QDebug>
#include <QWaitCondition>
#include <QMutex>
QWaitCondition condition;
QMutex mutex;
bool ready = false;
class MyThread : public QThread
{
public:
void run() override
{
mutex.lock();
while (!ready) {
condition.wait(&mutex);
}
mutex.unlock();
qDebug() << "Thread " << QThread::currentThread() << " is running";
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
MyThread thread1;
MyThread thread2;
thread1.start();
thread2.start();
// 模擬一段耗時(shí)操作
QThread::currentThread()->sleep(2);
mutex.lock();
ready = true;
condition.wakeAll();
mutex.unlock();
thread1.wait();
thread2.wait();
return a.exec();
}
在這個(gè)示例中,我們創(chuàng)建了兩個(gè)線程來執(zhí)行一段耗時(shí)操作。在主線程中,我們首先將ready
變量設(shè)置為true,然后調(diào)用condition.wakeAll()
來喚醒所有等待在條件變量上的線程。
在每個(gè)線程的run()函數(shù)中,我們首先使用mutex.lock()
來鎖定互斥量。然后,線程進(jìn)入一個(gè)循環(huán),使用condition.wait(&mutex)
來等待條件變量。只有當(dāng)條件變量滿足時(shí),線程才會(huì)被喚醒。一旦被喚醒,線程會(huì)打印一條消息,并繼續(xù)執(zhí)行。
這樣,我們就實(shí)現(xiàn)了一種控制線程等待和喚醒的機(jī)制。主線程通過設(shè)置條件變量并喚醒等待的線程,實(shí)現(xiàn)了線程的同步。
3.多線程應(yīng)用
3.1服務(wù)器端編程
在Qt中,可以使用QTcpServer來實(shí)現(xiàn)多線程的服務(wù)器端編程。下面是一個(gè)簡單的示例:
#include <QCoreApplication>
#include <QTcpServer>
#include <QTcpSocket>
#include <QThreadPool>
#include <QDebug>
class MyThread : public QThread
{
public:
MyThread(qintptr socketDescriptor, QObject *parent = nullptr)
: QThread(parent), m_socketDescriptor(socketDescriptor)
{
}
void run() override
{
QTcpSocket socket;
if (!socket.setSocketDescriptor(m_socketDescriptor))
{
emit error(socket.error());
return;
}
qDebug() << "New connection:" << socket.peerAddress().toString() << ":" << socket.peerPort();
// 處理客戶端請求
QByteArray requestData = socket.readAll();
qDebug() << "Received data:" << requestData;
// 響應(yīng)客戶端請求
QByteArray responseData = "Hello from server!";
socket.write(responseData);
socket.waitForBytesWritten();
// 斷開連接
socket.disconnectFromHost();
socket.waitForDisconnected();
qDebug() << "Connection closed:" << socket.peerAddress().toString() << ":" << socket.peerPort();
}
signals:
void error(QAbstractSocket::SocketError socketError);
private:
qintptr m_socketDescriptor;
};
class MyServer : public QTcpServer
{
Q_OBJECT
public:
MyServer(QObject *parent = nullptr)
: QTcpServer(parent)
{
}
protected:
void incomingConnection(qintptr socketDescriptor) override
{
MyThread *thread = new MyThread(socketDescriptor, this);
connect(thread, &MyThread::finished, thread, &MyThread::deleteLater);
connect(thread, &MyThread::error, this, &MyServer::handleError);
thread->start();
}
private slots:
void handleError(QAbstractSocket::SocketError socketError)
{
qDebug() << "Socket error:" << socketError;
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
MyServer server;
if (!server.listen(QHostAddress::Any, 1234))
{
qDebug() << "Failed to start server!";
return -1;
}
qDebug() << "Server started, listening on port 1234...";
return a.exec();
}
#include "main.moc"
在這個(gè)示例中,我們創(chuàng)建了一個(gè)MyServer類,繼承自QTcpServer。在incomingConnection()函數(shù)中,每次有新的連接到來時(shí),我們創(chuàng)建一個(gè)新的MyThread線程來處理該連接。
MyThread類繼承自QThread,覆蓋了run()函數(shù)。在run()函數(shù)中,我們創(chuàng)建了一個(gè)QTcpSocket對象,并使用setSocketDescriptor()函數(shù)將其與傳入的套接字描述符關(guān)聯(lián)起來。然后,我們處理客戶端的請求,讀取請求數(shù)據(jù),對數(shù)據(jù)進(jìn)行處理,并向客戶端返回響應(yīng)數(shù)據(jù)。最后,斷開連接。
在main()函數(shù)中,我們創(chuàng)建了一個(gè)MyServer對象,并通過調(diào)用listen()函數(shù)來啟動(dòng)服務(wù)器。通過傳入監(jiān)聽的IP地址和端口號,來監(jiān)聽對應(yīng)的地址和端口。如果listen()成功啟動(dòng)服務(wù)器,則會(huì)在控制臺(tái)顯示對應(yīng)的提示信息。
這樣,我們就實(shí)現(xiàn)了一個(gè)多線程的服務(wù)器端程序。每次有新的連接到來時(shí),都會(huì)創(chuàng)建一個(gè)新的線程來處理該連接,實(shí)現(xiàn)了服務(wù)器的并發(fā)處理能力。
3.2客戶端編程
在Qt中,可以使用QThread來實(shí)現(xiàn)多線程的客戶端編程。下面是一個(gè)簡單的示例:
#include <QCoreApplication>
#include <QTcpSocket>
#include <QDebug>
#include <QThread>
class MyThread : public QThread
{
public:
MyThread(QObject *parent = nullptr)
: QThread(parent)
{
}
void run() override
{
QTcpSocket socket;
socket.connectToHost("127.0.0.1", 1234);
if(!socket.waitForConnected())
{
qDebug() << "Failed to connect to server!";
return;
}
// 發(fā)送請求
QByteArray requestData = "Hello from client!";
socket.write(requestData);
socket.waitForBytesWritten();
// 接收響應(yīng)
QByteArray responseData = socket.readAll();
qDebug() << "Received data: " << responseData;
// 斷開連接
socket.disconnectFromHost();
socket.waitForDisconnected();
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
MyThread thread;
thread.start();
return a.exec();
}
#include "main.moc"
在這個(gè)示例中,我們創(chuàng)建了一個(gè)MyThread類,繼承自QThread。在run()函數(shù)中,我們創(chuàng)建了一個(gè)QTcpSocket對象,并使用connectToHost()函數(shù)連接到服務(wù)器端。如果連接成功,則發(fā)送請求數(shù)據(jù),并等待響應(yīng)數(shù)據(jù)的返回。最后,斷開連接。
在main()函數(shù)中,我們創(chuàng)建了一個(gè)MyThread對象,并調(diào)用start()函數(shù)來啟動(dòng)線程。文章來源:http://www.zghlxwxcb.cn/news/detail-815546.html
這樣,我們就實(shí)現(xiàn)了一個(gè)多線程的客戶端程序。通過在獨(dú)立的線程中與服務(wù)器建立連接、發(fā)送請求和接收響應(yīng),實(shí)現(xiàn)了客戶端的并發(fā)操作能力。文章來源地址http://www.zghlxwxcb.cn/news/detail-815546.html
到了這里,關(guān)于QT基礎(chǔ)篇(12)QT5多線程的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!