问题由来:今天要完成一个采集视频并且保存的小程序,主界面VideoCapture继承自QWidget。主界面显示的时候后台也要连续不断地采集视频并保存,根据经验这个时候需要定义一个采集的线程类CaptureThread,所以主界面类中有一个CaptureThread类的成员变量*m_captureThread,该变量为一个指针。
实现的时候,VideoCapture类的构造函数里先new一个CaptureThread对象,然后开启采集线程,m_captureThread->start();
主函数里
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
VideoCapture w; // 这个类里面创建了一个线程
w.show();
return a.exec(); // 进入循环
}
程序编写好之后,运行一下发现没有问题。这时候忽然想到有个问题:如果界面类w被析构了,里面的线程怎么办,程序会不会爆掉!。。
这时候就涉及到Qt中线程的生命期问题。
下面结合狄泰软件学院的课程:《QT实验分析教程》里面第82课——线程的生命期问题进行分析。
、、、、、、、、、、、、、、、、、、、、、、
*狄泰软件学院唐老师的课程淘宝地址是:
狄泰唐老师的Qt实验分析视频教程(嵌入式GUI之Qt开发 比Mfc好用)-淘宝网
https://item.taobao.com/item.htm?spm=a230r.1.14.10.91f49cafIBmuJ&id=554046874548&ns=1&abbucket=19#detail*
、、、、、、、、、、、、、、、、、、、、、、
一下是我学习该课后自己的总结。
一个直观地理解,假设定义一个线程类A,A被析构之前,A中的线程函数一定要被执行结束,否则程序就会出问题。因为在线程函数中可能会调用到已经被析构了的成员变量,既然线程在C++里就是一个类,是类那么就会有生命周期。
使用准则:线程对象的生命周期 > 对应的线程生命周期*(Important)*
贴一张课程里面的图:
这张图是不是言简意赅呢!
为了保证这条准则能够被实现,在程序中有两种方法:
(1)同步型线程设计;
(2)异步型线程设计。
这两个名词看起来不是很理解,其实实现很简单,下面说明一下具体是怎么使用的。
同步型线程设计
概念:线程对象主动等待线程生命期结束后才销毁。
比如上面的视频采集线程,如果在delete m_captureThread;之后线程函数仍然在运行,那么程序就会出问题。
解决方案是:在CaptureThread类的析构函数中等待线程run函数执行完,添加一个wait()函数,强制等到线程运行结束。
这种设计支持在栈和堆中创建线程对象。如果线程对象在栈中,对象在自动被析构的时候调用构造函数,由于wait函数的存在,析构函数要等待线程函数执行完。如果线程对象在堆中,那么在delete m_captureThread之后也会调用析构函数~CaptureThread(),这时候也要等待线程函数执行完。
这样设计的话就保证了上面的使用准则。
使用场合:线程的生命周期相对较短的情形。(因为要等待,不要让析构函数等太久)
异步型线程设计
概念:线程生命期结束时通知销毁线程对象。
特点:
(1)只能在堆中创建线程对象;
(2)线程对象不能被外界主动销毁(这对应了特点1)。
实现方式:
在run()中最后调用deleteLater()函数。线程体函数主动申请销毁线程对象。
如:
void AsyncThread::run()
{
qDebug() << "void AsyncThread::run() tid = " << currentThreadId();
for(int i=0; i<3; i++)
{
qDebug() << "void AsyncThread::run() i = " << i;
sleep(1);
}
qDebug() << "void AsyncThread::run() end";
deleteLater();
}
析构函数:
AsyncThread::~AsyncThread()
{
qDebug() << "AsyncThread::~AsyncThread() destroy thread object";
}
调用者:
void async_thread()
{
AsyncThread* at = AsyncThread::NewInstance();
at->start();
}
输出:
main() tid = 0x33d4
void AsyncThread::run() tid = 0x389c
void AsyncThread::run() i = 0
void AsyncThread::run() i = 1
void AsyncThread::run() i = 2
void AsyncThread::run() end
AsyncThread::~AsyncThread() destroy thread object
在上面的代码中,程序中并没有主动delete掉这个对象,但是线程对象的析构函数被调用了(AsyncThread::~AsyncThread() destroy thread object被打印),这是为什么呢?
因为在线程体run()函数最后调用了 deleteLater();这样Qt平台会自动回收这个线程类,调用到它的析构函数。
看一下deleteLater();函数的实现:
void QObject::deleteLater()
{
QCoreApplication::postEvent(this, new QEvent(QEvent::DeferredDelete));
}
以及函数说明:
Schedules this object for deletion.
The object will be deleted when control returns to the event loop. If the event loop is not running when this function is called (e.g. deleteLater() is called on an object before QCoreApplication::exec()), the object will be deleted once the event loop is started.
Note that entering and leaving a new event loop (e.g., by opening a modal dialog) will not perform the deferred deletion; for the object to be deleted, the control must return to the event loop from which deleteLater() was called.
Note: It is safe to call this function more than once; when the first deferred deletion event is delivered, any pending events for the object are removed from the event queue.
See also destroyed() and QPointer.
该函数调用后并没有将对象立即销毁,而是想里面的参数一下DeferredDelete推迟销毁。函数被调用后,它会向主消息循环发送一个事件,主消息循环收到这个事件后才回去销毁这个对象,防止内存泄漏,同时也说明了使用异步型线程设计不需要调用者自己delete这个对象。
异步型线程设计使用的场合是线程生命期不可控,需要长时间运行于后台的情形。
在我的这个程序里,使用的是同步型线程设计。没有无缘无故的对(成功),也没有无缘无故的错(失败),编程是这样,学习工作做事也是这样,要掌握原理。拿昨天在学校南门看到的四个字——求实创新来说,求实需要学习和做事的过程当中掌握事物的本质,要知其然也要知其所以然。
推荐一下这里的课程,课程的qq入口群是:199546072
由于时间限制,本想好好宣传一下狄泰软件学院的课程,之后有时间了好好推荐一下,让更多学习者受益。

732

被折叠的 条评论
为什么被折叠?



