目录
1.1 QThread使用方法
1.2 QSemaphore使用方法
1.3 QWaitCondition使用方法
1.4 QThreadpool使用方法
1.5 QTConcurrent使用方法
前言:目前项目中使用到QSemaphore来构建线程池,之前只是简单使用QThread类,这里系统记录多线程的基本使用,方便后面使用到的时候翻开回忆,不再到处搜索知识点。
线程:进程是cpu、内存等资源占用的基本单位,它是应用程序实例(线程)要使用资源的集合。如果一个App没有创建多线程,那么它只有一个线程即主线程。进程之间相互独立,线程之间共享进程资源,通信便利,进程是线程的运行环境。线程有5种状态分别是创建建、运行、阻塞(需要线程锁或条件变量)、计时等待和退出。
「创建」一个新线程,等待 CPU 来执行;当 CPU 来执行时,如果该线程需要等待另外某个事件被执行完后才能执行,那该线程此时是处于「阻塞」状态;如果不需要等待其他事件,线程就可以被「运行」了,也可以称为正在占用时间片;时间片用完后,线程会处于「就绪」状态,等待下一次时间片的到来;所有任务都完成后,线程就会进入「退出」状态,操作系统就会释放该线程所分配的资源。
线程同步:所谓协同步调,即多个线程打配合完成某项复杂工作。线程同步涉及的技术有线程锁(互斥对象Mutex),条件变量(condition)和信号量(semaphore)。
Qt中提供了三种在主线程之外创建工作线程的方法:1. 继承QThread;2.继承QObject,然后使用moveToThread(QThread * targetThread)将对象移动到工作线程中执行;3.继承QRunnable,并将创建的对象移动到QThreadPool中进行执行。
Qt官方建议仅仅在需要扩展QThread本身的功能时使用第一种方法,而执行一般的工作时可使用第2种或第3种方法。后两者的区别是第2种方法可以通过信号进行线程间的通信。QRunnable没有继承QObject,适合执行不关心执行进度或者不需要在中间环节进行控制的方法。
QThread 继承 QObject.。它可以发送started和finished信号,也提供了一些slot函数。
QObject 可以用于多线程,可以发送信号调用存在于其他线程的slot函数,也可以postevent给其他线程中的对象。之所以可以这样做,是因为每个线程都有自己的事件循环
QThread 的执行入口函数是run(),从该函数返回将结束线程;wait()函数是等待run()函数执行结束并返回,此时wait()返回值为true;创建线程的方法有两种,1.直接继承QThread; 2.使用QObject::moveToThread()将 QObject 对象移到新开的 QThread 线程对象中,这样 QObject 对象中所有的耗时操作都会在新线程中被执行。
class Worker : public QObject
{
Q_OBJECT
QThread workerThread;
public slots:
void doWork(const QString ¶meter) {
// ...
emit resultReady(result);
}
signals:
void resultReady(const QString &result);
};
class Controller : public QObject
{
Q_OBJECT
QThread workerThread;
public:
Controller() {
Worker *worker = new Worker;
worker->moveToThread(&workerThread);
connect(workerThread, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(this, SIGNAL(operate(QString)), worker, SLOT(doWork(QString)));
connect(worker, SIGNAL(resultReady(QString)), this, SLOT(handleResults(QString)));
workerThread.start();
}
~Controller() {
workerThread.quit();
workerThread.wait();
}
public slots:
void handleResults(const QString &);
signals:
void operate(const QString &);
};
QThread的接口函数:
bool isStopThread = false;
class Athread:public QThread{
public:
void stop(){isStopThread =true;}
void run()
{
while(!is_runnable) {
qDebug()<<"thread start:"<
引用:https://www.cnblogs.com/ybqjymy/p/12210967.html
QMutex实现了对共享变量安全访问的机制,但是一个Mutex只能锁住一个共享变量。QSemaphore解决了多线程对共享资源的竞争,资源能够获取多次,能够保护一定数量的同种资源。比如:使用Mutex, A线程锁住了S1,B线程只能等待A解锁才能访问S1;使用QSemaphore, A获取了S1资源,B想访问S资源,发现S1已经被A占有了且有剩余资源S2,可以访问S2
QSemaphore公有接口:
条件变量解决了两个线程同时访问某个共享区域/资源出现死锁问题;它是与Mutex锁一起使用的;wait方法其实包括两步,第一步解锁,第二步等待被唤醒;
QWaitCondition提供了2种基本操作包括4种方法:
#include "qsemaphorestudycase.h"
#include
#include
#include
#include
#include
#include
#include
#include
//-------------Macro---------------
#ifdef Q_WS_S60
const int DataSize = 300;
#else
const int DataSize = 100000;
#endif
const int BufferSize = 8192;
char buffer[BufferSize];
QSemaphore freeBytes(BufferSize);
QSemaphore usedBytes;
QMutex qmx;
QWaitCondition qwd;
//--------------class---------------------
class Producer:public QThread
{
public:
void
run()
{
qsrand(QTime(0,0,0).secsTo (QTime::currentTime ()));
for(int i=0;istackSize ()<key () == Qt::Key_A)
{
consumer.start ();
//consumer.wait();
}
if(keyevent->key () == Qt::Key_B)
{
producer.start ();
//producer.wait ();
}
//keyPressEvent (keyevent);
}
“进程的创建、撤销与切换存在着较大的时空开销”,因此出现了线程这种轻量级进程技术。如果还想进一步的降低系统资源开销,人们想出了一个办法,就是让执行完所有任务的线程不被销毁,让它处于“待命”的状态等待新的耗时操作“进驻”进来。Qt 提供了 QThreadPool 和 QRunnable 这两个类来对线程进行重复的使用。使用的方法一般是将耗时操作放入 QRunnable 类的 run() 函数中,然后整体把 QRunnable 对象扔到 QThreadPool 对象中就可以了。
QRunnalbe:能用到线程池的任务,基本上功能单一且并不需要和其他线程进行信号槽的通信
引用:https://zhuanlan.zhihu.com/p/52612180
任务的统一封装形式:QRunnable
QRunnable()
virtual ~QRunnable()
bool autoDelete() const
void setAutoDelete(bool autoDelete)
virtual void run() = 0
只需要继承QRunnable并且重写run函数,扔给线程池即可
class HelloWorldTask : public QRunnable
{
void run() override
{
qDebug() << "Hello world";
}
};
HelloWorldTask *task = new HelloWorldTask();
QThreadPool::globalInstance->start(task);
QThreadpool只有线程QThread和任务QRunnable两种数据容器来管理任务调度,线程池的本质就是协调两种容器之间数据的交互。线程池中的线程也进行了二次封装,由继承于 QThread 的 QThreadPoolThread 类表示。该类在 QThread 的基础上扩展了两个私有变量:QRunnable * 和 QThreadPoolPrivate *,一个用于存储当前线程要执行的任务,一个用于存储线程池的指针。
因为每个 Qt 程序或者说每个进程都有一个全局 QThreadPool 对象,所以 globalInstance() 函数用于获取该对象的指针,那么用的时候直接用该静态函数即可,显式的创建一个局部线程池也是可以的。
#include
#include
#include
#include
class Task : public QRunnable
{
public:
void run()
{
qDebug() << "Hello";
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Task *task = new Task();
QThreadPool::globalInstance()->start(task);
return a.exec();
}
//显示创建
#include
#include
#include
#include
class Task : public QRunnable
{
public:
void run()
{
// 每个任务执行10秒,每一秒都输出当前线程
for (int i = 0; i < 10; ++i) {
qDebug() << QThread::currentThread();
QThread::sleep(1);
}
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QThreadPool pool;
// 每隔1秒插入一个任务
for (int i = 0; i < 200; ++i) {
Task *task = new Task();
pool.start(task);
QThread::sleep(1);
}
return a.exec();
}
QThread 这种很底层的类,使用起来要考虑方方面面,用到底层的接口(如 QThread、信号量、互斥锁等),另外Qt 提供了高级函数来简化多线程编写,Qt Concurrent 。
- run 相关:执行函数用
- map 相关:处理容器中的每一项
- filter 相关:筛选容器中的每一项
//pro 文件添加“Qt += concurrent”并且在我们的 h 文件添加“#include ”
#include
#include
#include
void function()
{
qDebug() << "Hello world";
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QtConcurrent::run(function); //调用 run() 函数就可以在单独线程里运行
return a.exec();
}
//传参
extern void aFunction(int arg1, double arg2, const QString &string);
int a = 1;
double b = 2;
QString string = "hello";
QtConcurrent::run(aFunction, a, b, string);
//返回值
extern QString function();
QFuturen future = QtConcurrent::run(function);
QString result = future.result();
参考:
如何正确使用QThread https://www.cnblogs.com/liushui-sky/p/5829563.html