自定义线程需要继承QThread类并重写run虚函数,这样run函数中的代码就会运行在子线程中。
QThread对象管理着一个线程,并通过start函数启动这个线程
QMutex(互斥锁)
QMutex(互斥锁)提供多线程间串行访问共享资源的方式,本质上的基于互相排斥的锁,
也称为互斥量。
QMutex常用于在多线程中保护对象、数据结果或者代码段,在同一时刻只有一个线程可以拥有互斥量。
--lock():锁定互斥量,如果另外一个线程锁定了这个互斥量,它将阻塞执行直到其他线程解锁这个互斥量。
--unlock():解锁一个互斥量,需要与lock()配对使用
--trylock():试图锁定一个互斥量,如果成功锁定就返回true;如果其他线程已经锁定了这个互斥量,就返回false,但不阻塞程序执行。
QMutexLocker
QMutexLocker是一个简化了互斥量处理的类
QMutexLocker的构造函数接受一个互斥量作为参数并将其锁定,析构函数则将此互斥量解锁,所有在QMutexLocker实例变量的生存期内的代码段得到保护,自动进行互斥量的锁定和解锁。
QMutex互斥量的不足
1.每次只能有一个线程获得互斥量的权限。
2.如果在一个程序中有多个线程读取某个变量,使用互斥量时也必须排队。而实际上若只是读取一个变量,是可以让多个线程同时访问的,这样互斥量就会降低程序的性能。
QReadWriteLock(读写锁)
1.QReadWriteLock以读或写锁定的同步方法允许以读或写的方式保护一段代码
2.可以允许多个线程以只读方式同步访问资源,但是只要有一个线程在以写方式访问资源时,其他线程就必须等待直到写操作结束。
QReadWriteLock提供的主要的函数:
--lockForRead(),以只读方式锁定资源,如果有其他线程以写入方式锁定,这个函数会阻塞;
--lockForWrite(),以写入方式锁定资源,如果本线程或其他线程以读或写模式锁定资源,这个函数就阻塞;
--unlock(),解锁
--tryLockForRead(),是lockForRead()的非阻塞版本
--tryLockForWrite(),是lockForWrite()的非阻塞版本
QWaitCondition(条件等待)
1.QWaitCondition与QMutex结合,可以使一个线程在满足一定条件时通知其他多个线程,使他们及时作出响应,这样比只使用互斥量效率要高一些
2.QWaitCondition一般用于“生产者/消费者”模型中
QWaitCondition提供如下一些函数:
--wait(QMutex *lockedMutex),解锁互斥量lockedMutex,并阻塞等待唤醒条件,被唤醒后锁定lockedMutex并退出函数;
--wakeAll(),唤醒所有处于等待状态的线程,线程唤醒的顺序不确定,由操作系统的调度策略决定;
--wakeOne(),唤醒一个处于等待状态的线程,唤醒哪个线程不确定,由操作系统的调度策略决定
为了描述方便,这里假设主线程(非Qt的UI线程)调用Send()往通信口发送一个数据包,然后阻塞等待回包才继续往下执行。另一个线程(通信线程)不断从通信口中接收数据并解析成数据包,然后唤醒主线程。下面是按网上给的最简单的方法:
// 示例一
// 主线程
Send(&packet);
mutex.lock();
condition.wait(&mutex);
if (m_receivedPacket)
{
HandlePacket(m_receivedPacket); // 另一线程传来回包
}
mutex.unlock();
// 通信线程
m_receivedPacket = ParsePacket(buffer); // 将接收的数据解析成包
condition.wakeAll();
QSemaphore(信号量)
信号量也是一种常用的同步机制,它可以用来控制对共享资源的访问数量。与互斥锁不同,信号量可以允许多个线程同时访问共享资源。
<span style="font-family:Verdana, Geneva, Arial, Helvetica, sans-serif;">#include <QtCore>
#include <stdio.h>
#include <stdlib.h>
const int DataSize = 100000;
const int BufferSize = 8192;
char buffer[BufferSize];
QSemaphore freeBytes(BufferSize);//生产者初始拥有4096B数据缓存区
QSemaphore usedBytes(0);//消费者初始使用了0字节数据
class Producer : public QThread//生产者
{
public:
void run();
};
void Producer::run()
{
qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
for (int i = 0; i < DataSize; ++i) {
freeBytes.acquire();
buffer[i % BufferSize] = "ACGT"[(int)qrand() % 4];
usedBytes.release();
}//若buffer充满了消费者未读的数据,acquire()就会被阻塞,直到消费者开始消费这些数据
}
class Consumer : public QThread//消费者
{
public:
void run();
};
void Consumer::run()
{
for (int i = 0; i < DataSize; ++i) {
usedBytes.acquire();
fprintf(stderr, "%c", buffer[i % BufferSize]);
freeBytes.release();
}//若buffer没有任何可读数据,acquire()就会被阻塞,直到生产者生产一些数据
fprintf(stderr, "\n");
}
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
Producer producer;
Consumer consumer;
producer.start();
consumer.start();
producer.wait();
consumer.wait();
return 0;
}</span>