qt QThread、QMutex、QSemaphore
QThread:
创建线程(2种方式):
1:
1.1 新建一个继承 QTherad 的类myTherad(不能有ui),重写 run() 函数(真正新线程处理的地方,因为不方便随时调用所以一般是个while循环) 。
1.2 主线程 new 上面的那个类 myTherad* _myTherad = new myTherad(this)
1.3 开始运行 run() : _myTherad.start();
2(默认支持事件循环(Qt的许多非GUI类也需要事件循环支持,如QTimer、QTcpSocket),QThread要支持事件循环需要在QThread::run()中调用QThread::exec()来提供对消息循环的支持,否则那些需要事件循环支持的类都不能正常发送信号,因此如果要使用信号和槽,那就直接使用QObject来实现多线程):
2.1 先随意新建一个类 classTs继承QObject就好,类里面有些方法
2.2 classTs* ts = new classTs; // 这里不能用this,因为你整个是要给子线程的
2.3 QThread* _myTherad = new QThread(this); // 创建子线程
2.4 ts->moveToThread(_myTherad ); // 把你随意建的类移动到子线程里面去,现在那个类就是属于子线程的人了
2.5 _myTherad->start(); // 开启线程
现在开始classTs类里的函数就都是在子线程里面运行了,至于怎么触发classTs类的函数,一般用型号槽,比如我点击主页面的一个按钮执行classTs类里的 a 函数,a函数就是在子线程里执行的了
结束线程:
在run函数调用exit()或者quit()函数可以结束线程,或在主线程调用terminate或stopImmediately()强制结束线程。
// 主函数结束子线程: _myThread->requestInterruption(); // 最好加上让线程自己走完 _myThread->terminate(); // 强制结束,它没全部跑完也没关系 _myThread->wait(); // 等它完全结束 delete _myThread; _myThread = NULL;
// 局部线程结束: Q_SIGNALS: void started(QPrivateSignal); //开始前发射 void finished(QPrivateSignal); //结束时发射该信号 // 局部线程: m_printThread = new QPrintThread(NULL);//局部线程,没有父对象 m_printThread->start(); // 退出: m_printThread->stopImmediately(); connect(m_printThread,&QPrintThread::finished,m_printThread,&QObject::deleteLater);//自动销毁分配的内存 connect(m_pSaveThread,&SaveDataThread::destroyed,this,&MainWindow::SaveDataThreadDestroy); //SaveDataThreadDestroy自定义的槽函数 // 用一个临时变量记录当前正在运行的局部线程,由于线程结束时会销毁自己,因此要通知主线程把这个保存线程指针的临时变量设置为NULL 因此用到了QObject::destroyed信号,在线程对象析构时通知UI把m_printThread 设置为nullptr; void MainWindow::SaveDataThreadDestroy(QObject *obj) { if(qobject_cast<QObject*>(m_pSaveThread) == obj ) { m_pSaveThread = NULL; } }
全局线程和局部线程的区别
1.全局线程需要调wait阻塞等待,防止主线程窗口销毁了,把线程对象也释放了,而线程还没出来,会引起崩溃。
2.局部线程退出时,需要通知主线程把线程指针置NULL
QMutex:
void myThread::run() { qDebug() << "myThread run start."; int a = 0; while (!isInterruptionRequested()) { // QMutex会带来一定的耗时,大概速度会降低1.5倍(Release模式) mutex.lock(); // 确保同一时间只有一个线程可以运行lock与unlock之间的内容 sleep(1); a++; qDebug() << " myThread run while:" << a << " currentThreadId:" << currentThreadId() ; mutex.unlock(); sleep(1); } qDebug() << "myThread run end."; } // 主线程 _myThread->mutex.lock(); // 主线程得到锁,那 myThread 就一直卡在 mutex.lock() 直到得到锁, 一把锁只能一个人得到,得到者才能操作。 QMutexLocker lock(&mutex) 得到加完了会自动解锁 /* lock: 阻塞式的,得不到锁就一直等着得到它,只有等别人 unlock 才能得到 tryLock: 非阻塞式。如果获取锁失败(即锁已被其他线程获取),立即返回false否则立即返回true。传参得话,表示的是时间,加了就根据时间来了,就不立即了返回了 QWaitCondition: wait(&mutex):主动解锁,然后把自己阻塞,等着 wakeAll 或wakeOne(唤醒一个等待线程,具体哪一个线程,由操作系统决定) 来解救才继续往下执行. 所以调用wait函数前,必须是要有锁的 QReadWriteLock: 读写锁 特点:可以多个线程以只读方式访问,但只有一个线程以写的方式访问。 LockForRead(); 如果有其他线程以写入的方式锁定,才阻塞自己 LockForWrite(); 阻塞自己 注:QReadlocker 和QWriteLocker是QReadWriteLock的简化,如同QMutexLocker 是QMutex 的简化一样。无须使用unlock(). */
// 生产者消费者 这种就是同一时刻只能有一个线程操作数据(写或者读) // 全局变量 QMutex mutex; QWaitCondition bufferEmpty; QWaitCondition bufferFull; const int DataSize = 1000; const int BufferSize = 80; int buffer[BufferSize]; int numUsedBytes = 0; // 已经生产的数据 int rIndex = 0; // 生产者 void Producer::run() { for (int i = 0; i < DataSize; i++) { mutex.lock(); if (numUsedBytes == BufferSize) bufferEmpty.wait(&mutex); //容量已满,先解锁mutex,让其它线程能够使用mutex,然后自己阻塞,等着wakeAll buffer[i%BufferSize] = i%BufferSize; numUsedBytes++; //用来记录当前缓冲区是否已满 bufferFull.wakeAll(); //缓冲区有容量,唤醒消费者 mutex.unlock(); } } // 消费者 void Comsumer::run() { for (int i = 0; i < DataSize; i++) { mutex.lock(); if (numUsedBytes == 0) bufferFull.wait(&mutex); //容量为空,阻塞消费者 qDebug() << currentThreadId() << buffer[i%BufferSize]; numUsedBytes--; bufferEmpty.wakeAll(); //缓冲区未满,唤醒生产者 mutex.unlock(); } }
QSemaphore: 一个通用的可计数的信号量,信号量实际上就是广义的互斥量。一个互斥量只能被锁定一次,然而一个信号量可以被获取多次。信号量常被用于保护一定数目的同类资源。
基本操作:
QSemaphore::QSemaphore ( int n = 0 ) : 新建一个信号量,守护的资源数为n(默认为0)
QSemaphore::~QSemaphore ():销毁信号量警告:销毁一个正在使用的信号量将导致一个不确定的行为
int QSemaphore::available() const :用于返回可用资源的数目
void QSemaphore::acquire ( int n = 1 ):从该信号量守护的资源中获取n个资源。如果获取的资源数目大于available()返回值,那么自己将阻塞,直到可获取足够的资源数
void QSemaphore::release ( int n = 1 ):释放n个信号量守护的资源给信号量。该函数也可以用来增加一个信号量可使用的资源数目
bool QSemaphore::tryAcquire ( int n = 1 ):尝试从信号量守护的资源中获取n个资源。成功,则返回true。如果当前可用的资源数目不够立即返回,返回值为false,并且不占用任何资源
bool QSemaphore::tryAcquire ( int n, int timeout ):尝试从信号量守护的资源中获取n个资源。成功,则返回true。如果当前可用的资源数目available()不够,将等待timeout微秒。
例子:
QSemaphore sem(5); // sem.available() == 5 sem.acquire(3); // sem.available() == 2 sem.acquire(2); // sem.available() == 0 sem.release(5); // sem.available() == 5 sem.release(5); // sem.available() == 10 sem.tryAcquire(1); // sem.available() == 9, returns true sem.tryAcquire(250); // sem.available() == 9, returns false
// 生产者消费者 这种就是同一时刻可以有多个线程同时操作数据(一边写一边读) // 全局函数(使用类的static/extern(注意.h声明 .cpp定义)/线程类在同一文件) const int DataSize = 1000; //生产者将生成的数据量 const int BufferSize = 800; //循环缓冲区的大小,该值小于DataSize char buffer[BufferSize]; QSemaphore freeData(BufferSize); // 空闲的可供生产的数据,就是假设消费者一直抢不到cpu那生产者最多也只能生产到800就停止了,只有等消费者消费了一个生产者才能生产第二轮的第一个 QSemaphore usedData(0); // 可供读取使用的数据 // 生产者线程 void Producer::run(){ for (int i = 0; i < DataSize; ++i) { freeData.acquire(); // 生产完了一个,这样生产的可用资源少一个 799 ...... qDebug() << "生产:thread " << QThread::currentThreadId() ;
buffer[i%BufferSize] = QString::number(i).at(0).unicode(); usedData.release(); // 这样消费者的资源多一个 1 ...... } } // 消费者线程 void Consumer::run(){ for (int i = 0; i < FxGlobal::DataSize; ++i) { usedData.acquire(); // 消费完了一个,这样消费者的资源少一个 0 ...... qDebug() << "消费:thread " << QThread::currentThreadId() ;
fprintf(stderr, "%c", buffer[1%BufferSize]); freeData.release(); // 这样生产的可用资源多一个 800 ...... } }
// 一个生产者多个消费者 // 初始化全局变量 //互斥锁 QMutex mutex; //单个生产者消费者使用变量 const int DataSize = 60; const int BufferSize = 10; //char buffer[BufferSize]; //产品数量 QSemaphore freeSpace(BufferSize); QSemaphore usedSpace(0); //缓冲区可以放10个产品 int buffer[10]; int numtaken=60; int takei=0; // 生产者线程 void mythread::run() { for (int i = 0; i < DataSize; ++i) { freeSpace.acquire(); qDebug() << "生产:thread "<<QThread::currentThreadId(); usedSpace.release(); sleep(1); } } // 消费者线程 void WriterThread::run() { //函数获取当前进程的ID while(numtaken>1) { usedSpace.acquire(); //P(s2)操作原语 mutex.lock(); //从缓冲区取出一个产品...多个消费者,不能同时取出,故用了互斥锁 printf("thread %ul take %d from buffer[%d] \n",currentThreadId(),buffer[takei%10],takei%10); qDebug() << "消费:thread "<<QThread::currentThreadId()<<"take "+QString::number(takei%10)+" from buffer["+QString::number(buffer[takei%10])+"] \n"; takei++; numtaken--; mutex.unlock(); usedSpace.release(); //V(s1)操作原语 //sleep(1); } }
本文来自博客园,作者:封兴旺,转载请注明原文链接:https://www.cnblogs.com/fxw1/p/17266153.html