5. Qt深入 线程例子
例子为Qt文档中的例子
https://doc.qt.io/qt-6/zh/qtcore-threads-mandelbrot-example.html
知识点
前提,该线程的实现是继承QThread类,重写run函数,取名RenderThread。
QThread只是一个线程管理类,注意它只是管理一个线程的,这个类本身并不是一个线程。
1. 在RenderThread的析构函数中控制线程退出
在这种线程实现方式中,除了run函数,其他该类的函数包括构造函数、析构函数、未被run调用的成员函数都是在父线程中运行的,所以我们可以在RunderThread析构函数中设置控制run循环的变量使run函数退出,也就是使子线程退出。这样在delete RenderThread对象的时候,就自动在析构函数中控制子线程退出了
2. RenderThread类中的变量需要加锁
如下代码中定义的abort、restart变量,run函数和类内的其他函数(构造函数、成员函数等)都可以访问,而run函数是运行在子线程,其他函数运行在主线程,所以访问这些变量的时候就涉及线程安全问题,所以对于run函数和其他函数都会访问的变量,需要加锁
3. 子线程没有任务时,可以使用条件变量使子线程休眠,有任务时,主线程将子线程唤醒即可
4. RenderThread构造函数中启动线程
与1同理,由于构造函数是运行在主线程,所以可以在构造函数中启动子线程。这样在创建RenderThread对象的时候,就同时启动了子线程,在销毁RenderThread对象时,就自动退出了子线程。
// 以下代码对Qt文档中的例子做了精简
class RenderThread : public QThread
{RenderThread(){start(); //调用QThread.start,启动子线程}~RenderThread() //在析构函数中控制run函数子线程退出{mutex.lock(); //下面要访问共同的变量,需要加锁abort = true; //使run中的while循环退出condition.wakeOne();mutex.unlock();wait();}void wakeupThread() {restart = false; condition.wakeOne();} //主线程调用此函数,唤醒子线程void run(){while(!abort) {...... //业务逻辑mutex.lock();if (!restart)condition.wait(&mutex); //没有任务了,利用条件变量休眠mutex.unlock();}}
private:bool abort;bool restart;QMutex mutex;
};

5. 优化互斥锁加锁的内容
当子线程需要长时间使用共享变量进行业务处理时,可以把共享变量拷贝到临时变量,这样就不需要对整个业务处理加锁,只需要对变量拷贝部分加锁即可
//主线程子线程都会访问centerX/centerY
void RenderThread::setCenterPoint(double x, double y) //主线程会调用
{mutex.lock();this->centerX = x;this->centerY = y;mutex.unlock();
}
void RenderThread::run()
{mutex.lock();const double centerX = this->centerX; //将共享变量拷贝到临时变量,减小运行加锁内容的时间const double centerY = this->centerY;mutex.unlock();......//需要使用centerX/centerY的耗时操作
}

