Qt多线程同步机制
在多线程编程中,常常需要确保多个线程对共享资源的访问不会产生数据竞争。为此,我们使用同步机制来保证线程安全。在Qt/C++中,常见的同步机制包括
互斥锁(QMutex、std::mutex)、
信号量(QSemaphore)、
读写锁(QReadWriteLock)、
原子操作(QAtomicInt 等)
条件变量(QWaitCondition、std::condition_variable)
将详细介绍这些机制,配合代码示例和注释,帮助你理解这些工具在多线程中的应用。
1. 互斥锁(QMutex / std::mutex)
互斥锁是一种常见的同步工具,用于防止多个线程同时进入临界区(共享资源的代码段)。在任何时刻,只有一个线程可以持有互斥锁并进入临界区,其他线程必须等待锁被释放后才能继续执行。
示例代码及注释(QMutex)
#include <QMutex>#include <QThread>#include <iostream>QMutex mutex;int sharedResource = 0;class Worker : public QThread {public:void run() override {mutex.lock();std::cout << "Thread " << QThread::currentThreadId() << " is entering the critical section." << std::endl;sharedResource++;std::cout << "Shared Resource: " << sharedResource << std::endl;mutex.unlock();}};int main() {Worker worker1, worker2;worker1.start();worker2.start();worker1.wait();worker2.wait();return 0;}
-
互斥锁的锁定与解锁:使用
mutex.lock()锁定互斥锁,保证同一时刻只有一个线程能够进入修改sharedResource的临界区。执行完临界区的代码后,必须调用mutex.unlock()解锁。 -
线程竞争:两个线程
worker1和worker2竞争访问共享资源sharedResource,通过互斥锁保证安全。
2. 信号量(QSemaphore)
信号量是一种用于控制多个线程访问有限资源的同步机制。它允许多个线程进入临界区,但总数受到信号量的限制。可以看作是一个资源计数器,线程需要“获取”信号量才能继续执行,并在完成后“释放”信号量。
示例代码及注释(QSemaphore)
#include <QSemaphore>#include <QThread>#include <iostream>QSemaphore semaphore(3); // 允许同时有3个线程进入class Worker : public QThread {public:void run() override {semaphore.acquire(); // 获取信号量std::cout << "Thread " << QThread::currentThreadId() << " is entering." << std::endl;QThread::sleep(1); // 模拟工作std::cout << "Thread " << QThread::currentThreadId() << " is leaving." << std::endl;semaphore.release(); // 释放信号量}};int main() {Worker worker1, worker2, worker3, worker4;worker1.start();worker2.start();worker3.start();worker4.start();worker1.wait();worker2.wait();worker3.wait();worker4.wait();return 0;}
-
信号量的获取与释放:
semaphore.acquire()减少可用资源计数器,semaphore.release()增加资源计数器。只有计数器大于零时,线程才能继续执行。 -
并发限制:最多有3个线程可以同时进入临界区,超过的线程必须等待其他线程释放资源。
3. 读写锁(QReadWriteLock)
读写锁允许多个线程同时读取共享资源,但写操作是互斥的,即在写入时其他线程不能进行读或写操作。这适合多读少写的场景,提升了性能。
示例代码及注释(QReadWriteLock)
#include <QReadWriteLock>#include <QThread>#include <iostream>QReadWriteLock lock;int sharedResource = 0;class Reader : public QThread {public:void run() override {lock.lockForRead(); // 获取读锁std::cout << "Reader thread " << QThread::currentThreadId() << " reading: " << sharedResource << std::endl;lock.unlock(); // 释放读锁}};class Writer : public QThread {public:void run() override {lock.lockForWrite(); // 获取写锁sharedResource++;std::cout << "Writer thread " << QThread::currentThreadId() << " writing: " << sharedResource << std::endl;lock.unlock(); // 释放写锁}};int main() {Reader reader1, reader2;Writer writer1;reader1.start();reader2.start();writer1.start();reader1.wait();reader2.wait();writer1.wait();return 0;}
-
读写锁:
lock.lockForRead()允许多个线程同时读取,lock.lockForWrite()使得写入操作期间其他读写线程必须等待。 -
读写互斥:写线程
writer1阻塞其他读线程,直到写操作完成后其他线程才能继续读。
4. 原子操作(QAtomicInt / std::atomic)
原子操作是最轻量的同步机制之一,它通过硬件保证增减操作的原子性,不需要加锁。适合计数器等简单的共享数据操作。
示例代码及注释(QAtomicInt)
#include <QAtomicInt>#include <QThread>#include <iostream>QAtomicInt atomicCounter = 0;class Worker : public QThread {public:void run() override {for (int i = 0; i < 1000; ++i) {atomicCounter.ref(); // 原子增操作}}};int main() {Worker worker1, worker2;worker1.start();worker2.start();worker1.wait();worker2.wait();std::cout << "Final counter: " << atomicCounter.load() << std::endl; // 输出最终计数值return 0;}
-
原子性操作:
atomicCounter.ref()是一个原子操作,无需加锁。硬件保证它在多个线程间的安全性。 -
轻量同步:适合简单的增减计数操作,避免了使用锁带来的性能开销。
5. 条件变量(QWaitCondition /std::condition_variable)
条件变量用于线程间的同步,它允许线程等待某个条件的满足。当条件满足时,其他线程会被唤醒并继续执行。
示例代码及注释(QWaitCondition)
#include <QMutex>#include <QWaitCondition>#include <QThread>#include <iostream>QMutex mutex;QWaitCondition condition;bool ready = false;class Worker : public QThread {public:void run() override {mutex.lock();while (!ready) {condition.wait(&mutex); // 等待条件满足}std::cout << "Thread " << QThread::currentThreadId() << " is processing." << std::endl;mutex.unlock();}};int main() {Worker worker1, worker2;worker1.start();worker2.start();QThread::sleep(1); // 模拟准备时间mutex.lock();ready = true;condition.wakeAll(); // 唤醒所有等待线程mutex.unlock();worker1.wait();worker2.wait();return 0;}
-
条件变量等待:
condition.wait(&mutex)会使线程等待,并在等待期间释放互斥锁。一旦条件满足,线程会被唤醒并重新获取锁。 -
条件变量唤醒:
condition.wakeAll()唤醒所有等待线程,线程会检查条件是否已满足并继续执行。
在多线程编程中,选择合适的同步机制非常重要。根据不同场景和需求,互斥锁、信号量、读写锁、原子操作、条件变量各有其适用范围:
-
互斥锁 用于保护临界区,保证同一时刻只有一个线程访问共享资源。
-
信号量 适合控制对有限资源的并发访问。
-
读写锁 在多读少写的情况下能提供更好的性能。
-
原子操作 是轻量级的同步方式,适合简单的计数操作。
-
条件变量 则用于等待某个条件满足的线程间同步。
更多推荐



所有评论(0)