C++&QT 多线程调用全局变量的解决方案:互斥锁QMutex、信号量QSemaphore
在多线程运行中,各个线程之间难免会对同一个变量进行操作。假设线程thread_A和线程thread_B都会用到全局变量global_C。
互斥锁QMutex
QMutex目的是保护对象、结构体或者代码块,让其一次仅在一个线程里可以使用。
int global_C = 6;
void method_1()
{
global_C *= 5;
global_C /= 4;
}
void method_2()
{
global_C += 3;
global_C -= 2;
}
假设线程A调用method_1(),线程B调用method_2()。
依次调用两个线程:
// 当先运行线程A,后运行线程B,结果是:
// method_1()
global_C *= 5; //global_C 此时是30
global_C /= 4; //global_C 此时是7
// method_2()
global_C += 3; //global_C 此时是10
global_C -= 2; //global_C 此时是8
同时调用两个线程:
//当线程A和线程B同时运行时,其中一种结果可能是:
// 线程A运行method_1()
global_C *= 5; //global_C 此时是30
// 此时,极有可能操作系统让线程A进入休眠状态,使得线程B运行method_2()
global_C += 3; //global_C 此时是33
global_C -= 2; //global_C 此时是31
// 线程A运行method_1()
global_C /= 4; //global_C 此时是7
//当线程A和线程B同时运行时,其中一种结果也有可能是:
// 线程A运行method_1()
global_C *= 5; //global_C 此时是30
// 此时,极有可能操作系统让线程A进入休眠状态,使得线程B运行method_2()
global_C += 3; //global_C 此时是33
// 此时,极有可能操作系统让线程B进入休眠状态,使得线程A运行method_1()
global_C /= 4; //global_C 此时是8
// 线程B运行method_2()
global_C -= 2; //global_C 此时是6
此时,可以发现,当两个线程同时调用时,结果时混乱的。我们用QMutex来规避这种风险,任何时候,只有一个线程可以修改变量global_C ,从而保证结果的正确性。当线程A调用lock()时,线程B尝试调用lock()时将会阻塞,直到线程A调用unlock():
QMutex mutex;
int global_C = 6;
void method_1()
{
mutex.lock();
global_C *= 5;
global_C /= 4;
mutex.unlock();
}
void method_2()
{
mutex.lock();
global_C += 3;
global_C -= 2;
mutex.unlock();
}
信号量QSemaphore
QSemaphore是QMutex的泛化,QMutex只能锁一次,而QSemaphore可以多次获取信号量。类比来看,QMutex保护单个数量的同一个资源,而QSemaphore用来保护多个数量的同一个资源(有点抽象,可以简单的理解为把一个资源拷贝了好多份,但实际情况可能并不是这样)。
available() 返回当前可用的资源数量
acquire(int n) 尝试获取n个数量的同一个资源,如果n>available(),程序将会阻塞,直到获取到足够数量。
release(int n) 释放n个数量的资源。这里的释放指的是拿出n个数量的资源来使用,这样可以满足n个线程来使用这个资源(备注:这个函数也可以被当作"create" 资源来使用)。
tryAcquire(int n) 尝试获取n个数量的同一个资源,如果n<available(),获取n个资源,并返回true;如果n>available(),不获取任何资源,并返回false。
QSemaphore sem(10); //创建并初始化保护n个同一资源,10 == sem.available()
sem.acquire(3); //获取3个同一资源,7 == sem.available()
sem.acquire(6); //获取6个同一资源,1 == sem.available()
sem.release(4); //释放4个同一资源,5 == sem.available()
sem.release(5); //"create" 5 new resources,10 == sem.available()
sem.tryAcquire(11); //返回false,10 == sem.available()
sem.tryAcquire(2); //返回true,8 == sem.available()