关于QT程序跨线程信号与槽应用BUG
项目场景:跨线程信号与槽
在QT程序运行中,跨线程的信号与槽的运行机制与同一个线程的运行机制问题,类的对象移动到新线程之前创建的定时器对象不会随着类的位移而移动到新的线程,导致定时器在新线程任务程序不允许被调用。
问题描述:构造函数中实例化定时器
在类的构造函数中new的对象,会根据类存在的线程创建出来,但是在调用moveToThread的时候实例化的对象不会随着一起移动。在类的private中定义一个指针: QTimer * TestTime;然后再构造函数中实例化对象 TestTime = new QTimer; 如以下代码:
@Test.h
class Test :: public QObject{
Q_OBJECT
public:
explicit Test(QObject * parent = 0);
~Test();
public solts:
void TimerStar();
private:
QTimer * TestTime;
}
@Test.cpp
Test::Test(){
TestTime = new QTimer;
}
void Test::TimerStar(){
TestTimer->start(100);
}
@Main.cpp
Test *Function = new Test;
QThread *MyThread = new QThread;
Function->moveToThread(MyThread);
connect(this,SIGNAL(MyTimerStar()),Test,SLOT(TimerStar()));
emit MyTimerStar();
以上代码运行的时候会报错: Timers cannot be started from another thread
原因分析:实例化时机不对
本程序实际开启定时器的线程是MyThread,对象时MyTest开启的。但是会出现这个问题,有的人就会很疑惑,TestTime定时器指针是我的私有变量,怎么会不让开启定时器呢?
其实原因很简单,TestTime只是个定时器指针,他可以指定某一个定时器,之所以报“Timers cannot be started from another thread”并不是定时器指针不在当前操作的线程,而是定时器不在。
看到这里的朋友应该就会发现问题所在,这是由于定时器创建时机不对导致的问题,在“Function->moveToThread(MyThread);”把类的任务移动到线程之前,类先被实例化了出来,在实例化的同时会去执行类的构造函数,在执行构造函数的时候定时器被实例化了出来。也就是说定时器是被实例化到了Main主线程,而开启程序在MyThread中,这就导致了定时器指针确实可以指定到定时器的位置,也能够访问到定时器,但是却没有了定时器的开启权限。
解决方案:修改时机
修改定时器的创建时机,如在Test类中构建函数:
@Test.h
class Test :: public QObject{
Q_OBJECT
public:
explicit Test(QObject * parent = 0);
~Test();
public solts:
void TimerStar();
void TimerInit();//新构建的函数
private:
QTimer * TestTime;
}
@Test.cpp
Test::Test(){
}
void Test::TimerStar(){
TestTimer->start(100);
}
void Test::TimerInit(){//在新函数里面实例化定时器
TestTime = new QTimer;
}
@Main.cpp
Test *Function = new Test;
QThread *MyThread = new QThread;
Function->moveToThread(MyThread);
//在类的对象移动到线程之后再实例化,这个时候是用信号与槽,不可以直接调用函数。
//原因就是直接调用是在当前线程进行的任务,信号与槽是跨线程运行任务,显然我们是使用第二种。
connect(this,SIGNAL(MyTimerInit()),Test,SLOT(TimerInit()));
connect(this,SIGNAL(MyTimerStar()),Test,SLOT(TimerStar()));
emit MyTimerInit();
emit MyTimerStar();
如此这般操作,再次运行就不会出错。
在同一个线程的信号与槽调用就类似于if判断一样,跨线程运行信号与槽是堆栈执行的。
如:我快速点击一个按钮5次(快到线程跟不上),我每点击一下先给你一int变量+1,
再发送信号给另一个线程的槽函数输出这个值, 我快速点击之后,可能会输出结
果为(2,4,5,5,5),看明白这个就知道跨线程信号与槽是不能顺序执行的,除非用
变量互锁住。