使用互斥量时存在一个问题: 每次只能有一个线程获得互斥量的权限。如果在一个程序中有多个线程读取某个变量,使用互斥量时也必须排队。而实际上若只是读取一个变量,是可以让多个线程同时访问的,这样互斥量就会降低程序的性能。
例如,假设有一个数据采集程序,一个线程负责采集数据到缓冲区,一个线程负责读取缓冲区的数据并显示,另一个线程负责读取缓冲区的数据并保存到文件,示意代码如下:
int buffer [100];
QMutex mutex;
void threadDAQ::run()
{
...
mutex.lock();
get_data_and_write_in_buffer(); //数据写入buffer
mutex.unclock();
...
}
void threadShow::run()
{
...
mutex.lock();
show_buffer(); //读取buffer里的数据并显示
mutex.unclock();
...
}
void threadSaveFile::run()
{
...
mutex.lock();
Save_buffer_toFile(); //读取buffer里的数据并保存到文件
mutex.unclock();
...
}
数据缓冲区 buffer 和瓦斥量 mutex 都是全局变量,线程 threadDAO 将数据写到 buffer,线程threadShow 和 threadSaveFile 只是读取 buffer,但是因为使用互斥量,这3个线程任何时候都只能有一个线程可以访问 buffer。而实际上,threadShow 和 threadSaveFile 都只是读取 bufer 的数据,它们同时访问 buffer 是不会发生冲突的。
Qt 提供了 QReadWriteLock 类
,它是基于读或写的模式进行代码段锁定的,在多个线程读写一个共享数据时,可以解决上面所说的互斥量存在的问题。
QReadWriteLock 以读或写锁定的同步方法允许以读或写的方式保护一段代码,它可以允许多个线程以只读方式同步访问资源,但是只要有一个线程在以写方式访问资源时,其他线程就必须等待直到写操作结束。
QReadWriteLock 提供以下几个主要的函数:
- lockForRead(),以只读方式锁定资源,如果有其他线程以写入方式锁定,这个函数会阻塞:
- lockForWrite(),以写入方式锁定资源,如果本线程或其他线程以读或写模式锁定资源,这个函数就阻塞;
- unlock(),解锁;
- tryLockForRead(),是 lockForRead()的非阻塞版本;
- tryLockForWrite(),是lockForWrite的非阻塞版本。
使用QReadWriteLock,上面的三线程代码可以改写为如下的形式:
int buffer [100];
QReadWriteLock Lock;
void threadDAQ::run()
{
...
Lock.lockForWrite();
get_data_and_write_in_buffer(); //数据写入buffer
mutex.unclock();
...
}
void threadShow::run()
{
...
Lock.lockForRead();
show_buffer(); //读取buffer里的数据并显示
mutex.unclock();
...
}
void threadSaveFile::run()
{
...
Lock.lockForRead();
Save_buffer_toFile(); //读取buffer里的数据并保存到文件
mutex.unclock();
...
}
这样,如果 threadDAQ 没有以 lockForWrite()锁定 Lock,threadShow 和 threadSaveFile 可以同时访问 buffer,否则 threadShow 和 threadSaveFile 都被阻塞;如果 threadShow 和 threadSaveFile 都没有锁定,那么 threadDAQ 能以写入方式锁定,否则 threadDAQ 就被阻塞。
QReadLocker 和 QWriteLocker 是 QReadWriteLock 的简便形式,如同 QMutexLocker 是QMutex 的简便版本一样,无需与unlock()配对使用。使用QReadLocker 和QWriteLocker,则上面的代码改写为:
int buffer [100];
QReadWriteLock Lock;
void threadDAQ::run()
{
...
QWriteLocker Locker(&Lock);
get_data_and_write_in_buffer(); //数据写入buffer
...
}
void threadShow::run()
{
...
QReadLocker Locker(&Lock);
show_buffer(); //读取buffer里的数据并显示
...
}
void threadSaveFile::run()
{
...
QReadLocker Locker(&Lock);
Save_buffer_toFile(); //读取buffer里的数据并保存到文件
...
}