注:低级的多线程实现方式必要时需要解决线程同步问题。
QThread实现多线程目前有两种方式:继承QThread重新实现 virtual QThread::run()和使用
void QObject::moveToThread(QThread *thread)。
1:继承QThread重新实现 virtual QThread::run() :
注:该方法不被推荐使用,因为重写run()默认情况下使QThread管理的线程无法启用事件循环,即无法通过事件循环进行消息传递和事件处理和QThread应当被看做是新线程的入口,不应去改变该类的实现细节。
实现细目:
1:继承QThread: class MyThread : public QThread { ...... }
2:重写方法: [virtual protected] void QThread::run()
实现过程:
启动线程:[slot] void QThread::start(QThread::Priority priority = InheritPriority)
任务实现:void QThread::run( ){ //MyThread管理的线程所执行的任务
while( ! this->isInterruptionRequested()) { //见注1
// 实现细节,必要时需要进行线程同步
} //不一定是while方式执行任务和退出任务
}
结束线程:1:void QThread::requestInterruption( ) //请求线程中断
2:bool QThread::wait(unsigned long time = ULONG_MAX) //阻塞调用点,本线程等待MyThread所管理的线程退出。
unsigned long time = ULONG_MAX
默认无限等待直到MyThread所管理的线程退出,返回真。
设置等待毫秒数:若超时返回假,否则返回真。
注1:
bool QThread::isInterruptionRequested() const
当通过QThread::requestInterruption( )调用请求线程中断时,该函数会返回真,即请求中断成功,则退出相关操作。
注2: [slot] void QThread::terminate()
非必要不要使用该函数,它可能会立即停止线程任务在任何执行路径上,线程可能来不及释放锁和做相关的线程清理工作。
2:使用void QObject::moveToThread(QThread *thread)
注:推荐使用该方法实现低级的多线程控制。QThread只管理线程相关的任务,不实现实际任务,实际任务由QObject子类实现。该方法不重写QThread::run(),也可以进行消息循环处理跨线程消息和事件处理。
实现细目:
1: 继承QObject : class MyTask : public QObject{ ...... }
2: 使用QObject::moveToThread( ) : myTask::moveToThrad(new QThread(this)) //若该操作在QObject及其子类中
实现过程:
1:子类化QObject实现具体任务:
class MyTask : public QObject{ //伪代码
Q_OBJECT;
public:
//相关构造和析构
//......
void stopTask1{ this->stoped1=true; } //通过该调用退出任务1
void stopTask2{ this->stoped2=true; } //通过该调用退出任务2
public slots :
//相关任务:
void task1( ){
while( !this->stoped1){ //不一定是while方式执行任务和退出任务
//相关任务
}
}
void task2( ){
while( !this->stoped2){
//相关任务
}
}
//......
singles:
//启动相关任务:
void runTask1( );
void runTask2( );
//......
private:
bool stoped1=false; //任务1的退出标志位
bool stoped2=false; //任务2的退出标志位
}
2:实例化任务和连接相关控制:
QThread* myThread =new QThread();
MyTask* myTask=new MyTask(); //该类在移动之前不能存在在对象树上,否则不能移动到其他线程
myTask->moveToThread(myThread); //最好在连接之前移动对象
connect(myThread,&QThread::started,myTask,&MyTask::runTask1); //该连接为直接连接,因为QThread::started在MyThread管理的线程触发
connect(myTask,&MyTask::runTask1,myTask,&MyTask::task1);
connect(myTask,&MyTask::runTask2,myTask,&MyTask::task2);
connect(myThread,&QThread::finshed,myTask,&MyTask::deleteLater);
关于信号连接及其连接方式的决议请查看文章: QT信号槽的同步与异步执行_qt信号槽同步异步-优快云博客
myThread->start(); //启动线程, 开启任务1,在myThread管理的线程发出 started信号
//或myTask->thread()->start();
myTask->stopTask1( ); //停止任务1
myTask->runTask2( ); //开启任务2
myTask->stopTask2( ); //停止任务2
myTask->thread()->quit( ); //终止myThread管理的线程事件循环,意味着将要结束该线程,finished信号将在myThread管理的线程发出
myTask->thread()->wait( ); //阻塞本线程,等待myThread管理的线程退出
myThread->deleteLater( );
注:
相关任务应当通过信号槽的方式触发,不应该直接调用。
任务1的信号槽是直接连接的,因为QThread::started()信号,MyTask::runTask1( )信号都在myThread管理的线程触发。
任务2的信号槽是队列连接的,因为MyTask::runTask2( )信号在myThread对象所在的线程触发,myThread对象和其管理的线程不是同一个。
当QThread::finshed信号被发出时,事件循环已经结束,除了删除事件之外没有其他事件可处理。因此该信号可连接相关线程里的任务对象的清理工作。
当QThread::started信号被触发时意味着相关线程要执行但是QThread::run()还未开始调用,可以通过连接该信号执行相关线程任务。该信号是私有的,可以被连接但不能被调用触发。
注:本文为本人工作学习总结,若有错误恳请指正