在任何一门语言中,多线程都是一个相对其他方面比较重要的点,这里面的知识体系很庞大,同步和异步之间的处理方式,以及IO多路复用等等各种进行性能优化的方面,在往上层一点我们不可能一直进行系统层次的调用,这样太费时间也太麻烦,就到设计模式这里,比如反应器(Reactor)模式,再者多线程对代码的敏感程度较高,很对细微的改变可能会带来意向不到的效果,这更要求我们对于我们写的代码有更深次的理解,不仅仅是代码本身,还要求代码执行阶段锁遇到的各种问题,这就非常考验一个程序员的功底。
1.多线程及简单实例
QT5的多线程可以使用QtConcurrent和QThread两种方式来实现。
- 使用QtConcurrent: QtConcurrent提供了一种简单的方式来编写并行的代码。通过使用QtConcurrent::run()函数,可以在后台线程中执行一个函数。
#include#include 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类提供了一个线程对象,可以通过继承QThread来实现自定义的线程类。
#include#include 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()函数或者MyThread类的run()函数会在后台线程中执行,而主线程会继续执行下面的代码。
需要注意的是,当使用QThread时,不要直接调用run()函数,而是通过start()函数来启动线程。另外,在多线程编程中,需要注意线程安全性和共享资源的竞争问题。
2.多线程控制
2.1互斥量
在QT中,可以使用QMutex(互斥量)来控制多个线程对共享资源进行互斥访问,以避免竞争条件和数据损坏。
下面是一个使用QMutex的简单示例:
#include#include #include #include 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(); }
在这个示例中,我们创建了两个线程来递增sharedData变量的值。为了确保对sharedData的访问是互斥的,我们使用了mutex对象。在run()函数中,我们使用mutex.lock()来锁定互斥量,然后执行对共享资源的访问,最后使用mutex.unlock()来解锁互斥量。
这样就保证了同时只有一个线程可以访问共享资源,避免了数据损坏。
2.2信号量
在QT中,可以使用QSemaphore(信号量)来控制多个线程对资源的访问。信号量允许指定一个资源的数量,线程可以通过获取和释放信号量来控制对资源的访问。
下面是一个使用QSemaphore的简单示例:
#include#include #include #include 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(); }
在这个示例中,我们创建了两个线程来递增sharedData变量的值。为了确保对sharedData的访问是互斥的,我们使用了一个信号量semaphore,并将其初始化为1。
在每个线程的run()函数中,我们首先使用semaphore.acquire()来等待获取信号量。一旦线程获取到信号量,它就可以访问共享资源,进行对sharedData的递增操作。最后,线程使用semaphore.release()来释放信号量。
由于我们将信号量初始化为1,所以只有一个线程能够获取到信号量,而另一个线程必须等待。这样就确保了对共享资源的访问是互斥的。
2.3线程等待及唤醒
在Qt中,可以使用QWaitCondition类来控制线程的等待和唤醒。QWaitCondition提供了一个条件变量,可以让线程在某个条件满足时等待,并在条件满足时被唤醒。
下面是一个使用QWaitCondition的简单示例:
#include#include #include #include #include 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(); // 模拟一段耗时操作 QThread::currentThread()->sleep(2); mutex.lock(); ready = true; condition.wakeAll(); mutex.unlock(); thread1.wait(); thread2.wait(); return a.exec(); }
在这个示例中,我们创建了两个线程来执行一段耗时操作。在主线程中,我们首先将ready变量设置为true,然后调用condition.wakeAll()来唤醒所有等待在条件变量上的线程。
在每个线程的run()函数中,我们首先使用mutex.lock()来锁定互斥量。然后,线程进入一个循环,使用condition.wait(&mutex)来等待条件变量。只有当条件变量满足时,线程才会被唤醒。一旦被唤醒,线程会打印一条消息,并继续执行。
这样,我们就实现了一种控制线程等待和唤醒的机制。主线程通过设置条件变量并唤醒等待的线程,实现了线程的同步。
3.多线程应用
3.1服务器端编程
在Qt中,可以使用QTcpServer来实现多线程的服务器端编程。下面是一个简单的示例:
#include#include #include #include #include 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; // 响应客户端请求 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"
在这个示例中,我们创建了一个MyServer类,继承自QTcpServer。在incomingConnection()函数中,每次有新的连接到来时,我们创建一个新的MyThread线程来处理该连接。
MyThread类继承自QThread,覆盖了run()函数。在run()函数中,我们创建了一个QTcpSocket对象,并使用setSocketDescriptor()函数将其与传入的套接字描述符关联起来。然后,我们处理客户端的请求,读取请求数据,对数据进行处理,并向客户端返回响应数据。最后,断开连接。
在main()函数中,我们创建了一个MyServer对象,并通过调用listen()函数来启动服务器。通过传入监听的IP地址和端口号,来监听对应的地址和端口。如果listen()成功启动服务器,则会在控制台显示对应的提示信息。
这样,我们就实现了一个多线程的服务器端程序。每次有新的连接到来时,都会创建一个新的线程来处理该连接,实现了服务器的并发处理能力。
3.2客户端编程
在Qt中,可以使用QThread来实现多线程的客户端编程。下面是一个简单的示例:
#include#include #include #include 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; } // 发送请求 QByteArray requestData = "Hello from client!"; socket.write(requestData); socket.waitForBytesWritten(); // 接收响应 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"
在这个示例中,我们创建了一个MyThread类,继承自QThread。在run()函数中,我们创建了一个QTcpSocket对象,并使用connectToHost()函数连接到服务器端。如果连接成功,则发送请求数据,并等待响应数据的返回。最后,断开连接。
在main()函数中,我们创建了一个MyThread对象,并调用start()函数来启动线程。
这样,我们就实现了一个多线程的客户端程序。通过在独立的线程中与服务器建立连接、发送请求和接收响应,实现了客户端的并发操作能力。
猜你喜欢
网友评论
- 搜索
- 最新文章
- 热门文章