Qt - 线程的创建和使用QThread、QThreadPool、QtConcurrent

Qt中可以有多种使用线程的方式:

  1. 继承 QThread,重写 run() 接口;
  2. 继承 QObject,使用 moveToThread() 方法将 QObject 子类移至线程中,内部的所有使用信号槽的槽函数均在线程中执行;
  3. 使用 QThreadPool 线程池,搭配 QRunnable;
  4. 使用 QtConcurrent;

1.继承QThread

待补充

2.继承QObject,使用moveToThread

任务类代码如下:

class PrintTask : public QObject
{
    Q_OBJECT
public:
    PrintTask();
    ~PrintTask();

signals:
    void mySignal();

public slots:
    void RunTask();
    void Test();
    void StopTask();

public:
    volatile bool m_stop;
};

volatile 关键字:当多个线程都要用到某一个变量且该变量的值会被改变时应该用volatile声明,该关键字的作用是防止编译器优化把变量从内存装入CPU寄存器中;

isInterruptionRequested 和 requestInterruption配套使用。requestInterruption请求中断线程,但不会停止事件循环,isInterruptionRequested本质是加锁判断,所以不要频繁调用;

PrintTask::PrintTask()
{
    m_stop = false;
}

PrintTask::~PrintTask()
{
}

void PrintTask::RunTask()
{
    qDebug() << "RunTask 被调用线程ID为:" << QThread::currentThread();
    while(!QThread::currentThread()->isInterruptionRequested()) // 或者用!m_stop
    {
        Test();
        //QCoreApplication::processEvents();
        QThread::sleep(1);
    }
    qDebug() << "RunTask 退出:" << QThread::currentThread();
}

void PrintTask::StopTask()
{
    qDebug() << "StopTask()" << QThread::currentThread();
    m_stop = true;
}

void PrintTask::Test()
{
    qDebug() << "Test 被调用线程ID为:" << QThread::currentThread();
    emit mySignal();
}

主函数初始化,可以连接deleteLater让线程退出时自杀,尽量避免手动调用delete去删除对象。也可以在线程finished后手动处理。

	m_Thread = new QThread(this);
    m_printTask = new PrintTask();
    m_printTask->moveToThread(m_Thread);

    // 线程启动后调用RunTask
    //connect(m_Thread, &QThread::started, m_printTask, &PrintTask::RunTask);
    // 线程退出后删除任务对象
    //connect(m_Thread, &QThread::finished, m_printTask, &PrintTask::deleteLater);
    // 线程完成
    connect(m_Thread, &QThread::finished, this, &Widget::ThreadFinished);
    connect(this, &Widget::StartThread, m_printTask, &PrintTask::RunTask, Qt::QueuedConnection);

启动线程:

    m_Thread->start();
    // 调用任务的函数。要使用信号槽,不能直接调用任务的函数,直接调用是在主线程中运行;
    emit StartThread();

停止线程:

	m_Thread->requestInterruption(); // 或者调用任务的StopTask(),直接调用StopTask是在主线程中运行;
    m_Thread->quit();
    m_Thread->wait();

使用QMetaObject::invokeMethod异步执行,可以在worker所在线程执行方法;

class Worker : public QObject
{
    Q_OBJECT
public slots:
    void doWork()
    {
        // 执行耗时操作
        while (!m_stopFlag) {
            // do something
        }
    }

    void stopWork()
    {
        m_stopFlag = true;
    }

private:
    bool m_stopFlag = false;
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    Worker worker;
    QThread thread;
    worker.moveToThread(&thread);
    thread.start();

    // 请求线程停止并等待线程退出
    QMetaObject::invokeMethod(&worker, "stopWork", Qt::QueuedConnection);
    thread.quit();
    thread.wait();

    return a.exec();
}

3.使用 QThreadPool 线程池,搭配 QRunnable

QRunnable成员函数:

void QRunnable::setAutoDelete(bool autoDelete);

设置其传到底给线程池后,是否需要自动析构;

bool QRunnable::autoDelete() const;

若该值为false,则需要程序员手动析构,要注意内存泄漏;

QThreadPool成员函数:

void QThreadPool::start(QRunnable * runnable, int priority = 0);

start() 预定一个线程用于执行QRunnable接口,当预定的线程数量超出线程池的最大线程数后,QRunnable接口将会进入队列,等有空闲线程后再执行;priority指定优先级

bool QThreadPool::tryStart(QRunnable * runnable);

tryStart() 和 start() 的不同之处在于,当没有空闲线程后,不进入队列,返回false

bool tryTake(QRunnable *runnable)

删除队列中的一个QRunnable,若当前QRunnable 未启动则返回成功,正在运行则返回失败

void setMaxThreadCount(int maxThreadCount)

设置线程池维护的最大线程数量;若没有设定,默认值是最大线程数,可以用:QThread::idealThreadCount(); 获取;

int maxThreadCount() const

获取线程池维护的最大线程数量;

int activeThreadCount() const

当前的活动线程数量

void QThreadPool::clear();

清空队列中还没有执行的QRunnable;

bool QThreadPool::waitForDone(int msecs = -1);

等待所有线程结束并释放资源, msecs指定超时;
若所有线程都被移除,则,返回true,否则返回false;

void setExpiryTimeout(int expiryTimeout)

线程长时间未使用将会自动退出节约资源

int expiryTimeout() const

获取线程的终结超时时间;

void releaseThread()

释放被保留的线程

void reserveThread()

保留线程,此线程将不会占用最大线程数量,从而可能会引起当前活动线程数量大于最大线程数量的情况

static QThreadPool * QThreadPool::globalInstance();

全局内存池实例。若创建QThreadPool实例,则在实例生存周期内,内存池有效

示例:
任务类代码如下:

class MyTask : public QObject, public QRunnable
{
    Q_OBJECT

public:
    MyTask();
    ~MyTask();
    void stopTask();

protected:
    void run();

signals:
    //注意!要使用信号,采用QObejct 和 QRunnable多继承,记得QObject要放在前面
    void mySignal();

private:
    volatile bool m_stop;
};
MyTask::MyTask()
{
    m_stop = false;
}

MyTask::~MyTask()
{
    qDebug()<<"~MyTask()";
}

void MyTask::stopTask()
{
    m_stop = true;
    qDebug() << "MyTask::stopTask()" << QThread::currentThread();
}

//线程真正执行的内容
void MyTask::run()
{
    int count = 0;
    while(!m_stop) {
        QThread::sleep(1);
        if (++count >= 5) {
            return;
        }
    }

    qDebug() << "MyTask run 被调用,调用线程ID为:" << QThread::currentThread();
}

主线程使用定义:

	QThreadPool m_pool;
    QVector<QPointer<MyTask>> m_task; // 存放任务对象

启动:

//设置最大线程数为2的一个线程池
    m_pool.setMaxThreadCount(2);
    // 创建5个任务并保存对象
    for (int i = 0; i < 5; i++) {
        QPointer<MyTask> a(new MyTask());
        m_task.append(a);
        m_pool.start(a);
    }

终止:

	m_pool.clear();
    for(int i = 0; i < m_task.size();++i){
        qDebug() << m_task[i].isNull();
        if (!m_task[i].isNull()) { // QPointer智能指针isNull判断指针是否有效
            // m_task[i].data()->stopTask(); // 停止还在运行中的任务
        }
    }
    m_pool.waitForDone();
    qDebug()<<m_pool.activeThreadCount();

m_task中记录了任务对象,线程池默认是执行完任务后删除任务对象的,通过QPointer的IsNull()就知道哪个对象没有删除,以此判断任务是否结束;

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Qt提供了多种方式来实现多线程,其中不需要重写QThread的方法有以下几种: 1. QtConcurrentQtConcurrentQt提供的一种高级并行编程框架,它支持多线程、并行算法和Lambda表达式。使用QtConcurrent可以方便地实现并行计算,而无需显式地创建线程。例如,使用QtConcurrent::run()函数可以在新线程中执行一个函数,如下所示: ```cpp void myFunction(int parameter) { // do something } QtConcurrent::run(myFunction, parameter); ``` 2. QThreadPoolQThreadPoolQt提供的一个线程池类,它可以用来管理多个线程使用QThreadPool可以将任务提交到线程池中,由线程池自动分配线程来执行任务。例如,使用QThreadPool::globalInstance()函数可以获取全局线程池实例,如下所示: ```cpp void myTask(int parameter) { // do something } QThreadPool::globalInstance()->start(new MyTask(parameter)); ``` 3. QTimer:QTimer是Qt提供的一个定时器类,它可以用来定时执行任务。使用QTimer可以在主线程中执行任务,而无需创建线程。例如,使用QTimer::singleShot()函数可以在一定时间后执行一个函数,如下所示: ```cpp void myFunction() { // do something } QTimer::singleShot(1000, myFunction); ``` 以上三种方法都可以方便地实现多线程,而且不需要显式地创建线程或重写QThread类。但是,需要注意的是,在多线程编程中仍然需要注意线程安全性和避免竞争条件等问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值