一、多线程间的互斥
1、程序的临界资源与线程锁的数量关系
(1)、一般性原则:每一个临界资源都需要一个线程锁进行保护
2、死锁的概念
(1)、线程间相互等待资源而造成彼此无法继承执行
3、发生死锁的条件
(1)、系统中存在多个临界资源且临界资源不可抢占(每次只有一个线程使用)
(2)、线程需要多个临界资源才能继续执行
#include <QCoreApplication> #include <QThread> #include <QMutex> #include <QDebug> static QMutex g_mutex_1; static QMutex g_mutex_2; class ThreadA : public QThread { protected: void run() { while(true) { g_mutex_1.lock();//获取第一把锁后就会去等待第二把锁,但第二把锁可能已经被另一个线程获取,会一直死等 g_mutex_2.lock(); qDebug() << objectName() << " doing work"; g_mutex_2.unlock(); g_mutex_1.unlock(); } } }; class ThreadB : public QThread { protected: void run() { while(true) { g_mutex_2.lock(); g_mutex_1.lock(); qDebug() << objectName() << " doing work"; g_mutex_1.unlock(); g_mutex_2.unlock(); } } }; int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); ThreadA ta; ThreadB tb; ta.setObjectName("ta"); tb.setObjectName("tb"); ta.start(); tb.start(); return a.exec(); }
4、死锁的避免
(1)、对所有的临界资源都分配唯一一个序号(r1,r2,r3,...,rn)
(2)、对应的线程锁也分配同样的序号(m1,m2,m3,..., mn)
(3)、系统中的每个线程按照严格递增的次序请求资源
#include <QCoreApplication> #include <QThread> #include <QMutex> #include <QDebug> static QMutex g_mutex_1; static QMutex g_mutex_2; class ThreadA : public QThread { protected: void run() { while(true) { g_mutex_1.lock(); g_mutex_2.lock(); qDebug() << objectName() << " doing work"; g_mutex_2.unlock(); g_mutex_1.unlock(); } } }; class ThreadB : public QThread { protected: void run() { while(true) { g_mutex_1.lock();//都是递增获取资源 g_mutex_2.lock(); qDebug() << objectName() << " doing work"; g_mutex_2.unlock(); g_mutex_1.unlock(); } } }; int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); ThreadA ta; ThreadB tb; ta.setObjectName("ta"); tb.setObjectName("tb"); ta.start(); tb.start(); return a.exec(); }
5、信号量的概念
(1)、信号量是特殊的线程锁
(2)、信号量允许多个线程同时访问临界资源
(3)、Qt中直接支持信号量(QSemaphore)
#include <QCoreApplication> #include <QThread> #include <QDebug> #include <QSemaphore> const int SIZE = 5; unsigned char g_buff[SIZE] = {0}; QSemaphore g_sem_free(SIZE); QSemaphore g_sem_used(0); class Producer : public QThread { protected: void run() { while(true) { int value = qrand() % 256; g_sem_free.acquire();//生产了一个产品,需要生产的数量减1 for(int i=0; i<SIZE; i++) { if( !g_buff[i] ) { g_buff[i] = value; qDebug() << objectName() << " generate: {" << i << ", " << value << "}"; break; } } g_sem_used.release();//生产了一个产品,可用的产品就增1 sleep(1); } } }; class Customer : public QThread { protected: void run() { while(true) { g_sem_used.acquire();//消费了一个产品,可消费的产品就减1 for(int i=0; i<SIZE; i++) { if( g_buff[i] ) { int value = g_buff[i]; g_buff[i] = 0; qDebug() << objectName() << " generate: {" << i << ", " << value << "}"; break; } } g_sem_free.release();//消费了一个产品,所需要生产的产品就增1 sleep(2); } } }; int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); Producer p1; Producer p2; Producer p3; p1.setObjectName("p1"); p2.setObjectName("p2"); p3.setObjectName("p3"); Customer c1; Customer c2; c1.setObjectName("c1"); c2.setObjectName("c2"); p1.start(); p2.start(); p3.start(); c1.start(); c2.start(); return a.exec(); }
二、小结
(1)、多线程间相互等待资源将导致死锁
(2)、可以对临界资源进行编号的方法避免死锁
(3)、所有线程必须按照严格递增的次序请求资源
(4)、Qt中直接支持信号量(QSemaphore)
(5)、信号量允许多个线程同时访问临界资源