【QT多线程】---线程同步

线程同步的方式

  1. QMutex 互斥锁
  2. QReadWriteLock 读写锁
  3. QSemaphore 信号量
  4. QWaitCondition 条件等待

1.QMutex

创建一个QMutex对象:

QMutex mutex;

在需要保护共享资源的代码段之前,调用lock()方法锁定互斥体:

mutex.lock();

如果互斥体已被其他线程锁定,则当前线程将被阻塞,直到互斥体可用。

访问共享资源,进行必要的读取或写入操作。

在完成对共享资源的访问后,调用unlock()方法释放互斥体:

mutex.unlock();

这将允许其他线程获取互斥体并访问共享资源。

代码示例:

#include <QMutex>
#include <QThread>
#include <QDebug>

// 共享资源
int sharedValue = 0;

// 互斥体
QMutex mutex;

// 线程类
class MyThread : public QThread
{
public:
    void run() override
    {
        for (int i = 0; i < 5; ++i) {
            mutex.lock();
            ++sharedValue; // 对共享资源进行写操作
            qDebug() << "Thread ID:" << currentThreadId() << "Shared Value:" << sharedValue;
            mutex.unlock();
            msleep(1000);
        }
    }
};

int main()
{
    MyThread thread1;
    MyThread thread2;

    thread1.start();
    thread2.start();

    thread1.wait();
    thread2.wait();

    return 0;
}

2.QReadWriteLock 读写锁

QMutexLocker 构造函数接受一个互斥量作为参数并将其锁定,析构函数则将此互斥量解锁,所以在QMutexLocker实例变量的生存周期内的代码段得到保护,自动进行互斥量的锁定和解锁

  // 创建一个QMutexLocker对象,并传入要锁定的互斥锁
    QMutexLocker locker(&mutex);

QReadWriteLock是一种线程同步工具,用于实现读写锁定机制。它允许多个线程同时对共享资源进行读操作,但只允许一个线程进行写操作。这种机制可以提高并发性能,并且适用于读操作频繁但写操作较少的场景。

QReadWriteLock通常包含两个锁:读锁和写锁。当一个线程获取读锁时,其他线程也可以获取读锁,从而允许并发的读操作。当一个线程获取写锁时,其他线程无法获取读锁或写锁,从而保证了独占式的写操作。

#include <QCoreApplication>
#include <QReadWriteLock>
#include <QThread>

QReadWriteLock lock;
int sharedData = 0;

void readData()
{
    lock.lockForRead();
    // 读取共享资源
    qDebug() << "Read Data:" << sharedData;
    lock.unlock();
}

void writeData()
{
    lock.lockForWrite();
    // 修改共享资源
    sharedData++;
    qDebug() << "Write Data:" << sharedData;
    lock.unlock();
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    // 创建多个读线程和写线程
    QList<QThread*> threads;
    for (int i = 0; i < 3; ++i) {
        QThread* thread = new QThread(&a);
        threads.append(thread);
        QObject::connect(thread, &QThread::started, readData);
        thread->start();
    }

    for (int i = 0; i < 2; ++i) {
        QThread* thread = new QThread(&a);
        threads.append(thread);
        QObject::connect(thread, &QThread::started, writeData);
        thread->start();
    }

    // 等待所有线程执行完成
    for (QThread* thread : threads) {
        thread->wait();
    }

    return a.exec();
}

3.基于条件变量的线程同步

QWaitCondition与QMutex结合,可以使一个线程在满足一定条件时通知其他多个线程,使他们及时做出响应,这样比只使用互斥量的效率高一些,QWaitCondition一般用于"生产者\消费者"模型

QWaitCondition提供的函数
void wait(QMutex *mutex, unsigned long time = ULONG_MAX)

等待条件满足,直到其他线程调用wakeOne()或wakeAll()方法唤醒当前线程。该函数会自动释放传入的mutex,并在被唤醒后重新获得mutex。

void wakeAll()

唤醒所有等待在该条件上的线程。

void wakeOne()

唤醒一个等待在该条件上的线程。

bool wait(QMutex *mutex, unsigned long time = ULONG_MAX)

等待条件满足,直到其他线程调用wakeOne()或wakeAll()方法唤醒当前线程。该函数会自动释放传入的mutex,并在被唤醒后重新获得mutex。
与第一种wait函数不同的是,该函数返回一个bool值,表示等待是否成功。

代码示例:生产者消费者模型

#define DATASIZE 100
#define BUFFERSIZE 8

int buffer[BUFFERSIZE]; //缓冲区

QWaitCondition bufferNotEmpty;  //非空
QWaitCondition bufferNotFull;   //非满

QMutex mutex;  //互斥锁
int numUsable = 0; //可用数据量
int index = 0; //下表

//生产者线程
class ProducerThread: public QThread{
protected:
	void run( ){
		for(int i = 0;i < DATASIZE; i++)
		{
			mutex.lock();
			if(numUsable == BUFFERSSIZE)//缓冲区已满
			{
				qDebug()<<"缓冲区已满,等待....";
				bufferNotFull.wait(&mutex);//进入等待并释放mutex,等待条件成立
			}
			buffer[i % BUFFERSIZE] = i+1;//缓冲区未满,向缓冲区写入数字
			++numUsable;
			qDebug()<<"生产者产生了数据:"<<i+1;
			msleep((Qrand()%5+1)*100);
			mutex.unlock();
			bufferNotEmpty.wakeAll();
			//唤醒正在等待缓冲区有数据(bufferNotEmpty)条件成立的线程
		}
	}
}


//消费者线程
class ConsumerThread: Qthread()
{
protect:
	void run()
	{
		while(true)
		{
			mutex.lock();
			if(numUsable == 0)  //缓冲区已空
			qDebug<<"缓冲区已空,等待...";
			bufferNotEmpty.wait(&mutex); 
			//等待缓冲区有数据(bufferNotEmpty)条件成立
		}
		qDebug<<"消费者"<<currentThreadID()<<"消费了数据:"<<buffer[index];
		index = (++index)%BUFFERSIZE;
		--numUserable;
		msleep((Qrand()%5+1)*100);
			mutex.unlock();
			bufferNotFull.wakeAll();
		//唤醒等待缓冲有空位(bufferNotFull)条件成立的线程
	}
}

int main(int argc,char *argv[])
{
	QCoreApplication a(argc,argv);
	
	qsrand(QTime::currentTime().msec());
	
	ProducerThread producer;
	ConsumerThread consumer1;
	ConsumerThread sonsumer2;
	
	producer.start();
	consumer1.start();
	consumer2.start();
	
	producer.wait();
	consumer1.wait();
	consumer2.wait();
	//主线程中等待producer\consumer1\consumer2线程执行完毕并发送了finished()信号后才会继续执行后续代码。
}

4.基于信号量的同步

QSemaphore可以用来控制多个线程对共享资源的访问。QSemaphore维护了一个内部的计数器,用来表示可用的资源数量,线程可以通过获取和释放资源来实现同步。

QSemaphore使用情况:

  • 控制并发线程数:可以限制同时执行的线程数量,比如连接池中最大连接数的控制。
  • 线程间同步:可以用于线程之间的协调和同步,确保线程按照指定的顺序执行。
  • 信号量机制:可以用于实现生产者-消费者问题等场景。

常用函数:

  • 构造函数:QSemaphore(int n) - 创建一个信号量对象,并初始化计数器为n。
  • acquire():请求一个资源,如果当前计数器大于0,则立即返回;否则进入阻塞状态,直到有资源可用。
  • release():释放一个资源,增加计数器的值,并唤醒可能在等待资源的线程。
  • release(int n):释放多个资源,增加计数器的值,并唤醒可能在等待资源的线程。
  • available():获取当前可用的资源数量,即计数器的值。
  • acquireTimeout(int timeout):请求一个资源,如果当前计数器大于0,则立即返回;否则进入阻塞状态,直到有资源可用或者超过指定的超时时间。
#define DATASIZE 100
#define BUFFERSIZE 8

int buffer[BUFFERSIZE]; //缓冲区

QSemphore freeSpaace(BUFFERSIZE);//生产者信号量
QSemphore dataNum(0);//消费者信号量

//生产者线程

class ProducerThread: public QThread
{
protected:
	void run()
	{
		for(int i = 0;i < DATASIZE; i++)
		{
			freeSpace.acquire(1);//获取一个生产者信号量
			buffer[i % BUFFERSIZE] = i+1;
			qDebug()<<"生产者产生了数据:"<<i + 1;
			//生产者产生了一个数字,对于消费者而言,就是多了一个可供消费的数字
			dataNum.release(1);
			msleep((Qrand()%5+1)*100);
		}
	}
}

//消费者线程
class ConsumerThread: public Qthread
{
protected:
	void run()
	{
		for(int i = 0;i < DATASIZE; i++)
		{
			dataNum.acquire(1);//获取一个消费者信号量
			qDebug()<<"消费者消费了数据:"<<buffer[i % BUFFERSIZE];
			//消费者消费了一个数字,对于生产者而言多了一个可以生产的空位
			freeSpace.release(1);
			msleep((Qrand()%5+1)*100);
		}
	}
}


int main(int argc,char *argv[])
{
	QCoreApplication a(argc,argv);
	
	qsrand(QTime::currentTime().msec());
	
	ProducerThread producer;
	ConsumerThread consumer;

	producer.start();
	consumer.start();

	producer.wait();
	consumer.wait();
	


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值