C++ Qthread多线程意外结果
我想用我的代码实现以下几点:C++ Qthread多线程意外结果,c++,multithreading,qt,C++,Multithreading,Qt,我想用我的代码实现以下几点: 两个线程,同时向控制台打印“foo”和“bar”100次。秩序不重要 创建线程的对象需要在线程完成时获取信号。在现实生活中,这个信号将携带线程的结果,创建者需要等待这个结果 这是我为本例编写的代码: main.cpp 首先,我们创建这个对象和一个QThread对象。在堆或堆栈中创建QThread对象时要小心。这将导致两种不同的结束线程的方法 QThread stackThread; Object workerStack; QThread* heapThread =
QThread
对象。在堆或堆栈中创建QThread
对象时要小心。这将导致两种不同的结束线程的方法
QThread stackThread;
Object workerStack;
QThread* heapThread = new Qthread;
Object workerHeap;
connect(&workerStack, &Object::finish, &stackThread, &QThread::quit);
connect(&workerHeap, &Object::finish, heapThread, &QThread::quit);
创建线程和辅助对象后,只需将辅助对象移动到线程。这里没有魔法
workerStack.moveToThread(&stackThread);
workerHeap.moveToThread(heapThread);
然后连接一个信号,以便在调用QThread::start
时,对象开始工作:
connect(&stackThread, &QThread::started, &workerStack, &Object::job1);
connect(heapThread, &QThread::started, &workerHeap, &Object::job2);
您可以使用finish()
信号停止线程
QThread stackThread;
Object workerStack;
QThread* heapThread = new Qthread;
Object workerHeap;
connect(&workerStack, &Object::finish, &stackThread, &QThread::quit);
connect(&workerHeap, &Object::finish, heapThread, &QThread::quit);
您可能还需要一个信号槽,以便在堆中创建的线程可以在完成时删除自身。在堆栈中创建的线程不需要这样做
connect(&workerHeap, &Object::finish, heapThread, &QThread::deleteLater);
如果您的finish()
connect(&workerStack, &Object::finish, this, &THISSCOPE::catchFinish);
如果需要等待线程结果以继续程序,可以使用QEventLoop
。请不要使用不应该执行此任务的QThread::wait
函数。wait
函数只是阻止当前事件循环,这样您就永远无法捕捉到finish
信号
虽然使用QEventLoop
可以让您接收finish
信号,但这不是一个好的设计。有很多方法可以避免这种情况,但我在此不作详细说明。我只是用QEventLoop
来演示当前示波器如何接收信号。代码如下:
QEventLoop eventloop;
connect(&workerStack, &Object::finish, &eventloop, &QEventLoop::quit);
现在,我们可以在创建所有插槽后启动线程:
threadStack.start();
threadHeap.start();
如果需要等待结果,请启动eventloop:
eventloop.exec();
当线程完成时,在堆中创建的线程应该发出finish
信号并删除自身(因为您连接到了它的deleteLater
插槽)。但是,在堆栈上创建的线程有点棘手。如果主代码超出了此处的范围,则可能会出现错误“QThread在运行时被销毁”。这是因为主代码比QThread::quit
快一点。因此,您需要在这里放置一个QThread::wait
。wait
函数的真正用途是在作业完成后等待线程退出不是等待作业完成。最后,我们加上:
threadStack.wait();
您描述的不同行为和错误很难调试。但是,当线程的创建、销毁、开始和结束得到正确实现时,您可能会看到错误消失,行为更加一致
一些提示:
- 您不需要初始化QEventLoop实例。默认的QThread实现已经有一个eventLoop(读和)
- 启动线程的正确方法是调用。如果连接到Controller::startWrite()并删除
emit startWrite()代码>,程序将执行相同的任务
从您的示例中,您有(至少)2个解决方案来改进实施:
按原样使用textWriter类。在这种情况下,您可以创建一个QThread和一个textWriter,使用moveToThread将textWriter实例移动到QThread,将QThread::start连接到textWriter::write,并将textWriter::finish连接到QThread::quit,最后使用QThread::start()启动线程
另一个解决方案是修改textWriter。您可以将textWriter转换为QRunnable的子级,并将textWriter::write重命名为。然后使用QThreadPool启动执行
在任何情况下,我建议您阅读一篇总结,这可能会帮助您找到符合您具体情况的解决方案
祝你好运;) 错误太多了
除了安特万先前的回答之外,没有更多的评论
- 为堆栈上分配的对象调用
delete
是个坏主意。对象threadA
和threadB
位于控制器的堆栈上。当构造函数完成时,它们会自动删除,并且由于deleteLater()
的原因,它们会被第二次删除
- 在运行
eventloopA
和eventloopB
时,主事件循环被阻塞。因此,在两个线程都未完成之前,控制器
对象无法接收任何信号。不需要阻止控制器
构造函数。我猜阻止它的想法来自于线程对象被破坏的问题。但是,可以使用new
创建线程对象以避免该问题
- 一般来说,通过线程的
finished
信号删除线程是不好的。通常,应用程序可能在调用QThread::quit()
和发出QThread::finished()
信号之前关闭
请参见QThread
中的controller
示例<代码>QThread
可以在其他偶数循环中删除(不是它自己的循环)。但是,即使当主偶数循环(主线程)捕捉到线程QThread::finished()
信号时,QThread
仍可能正忙。为了确保线程在删除之前真正完成,有函数QThread::wait()
我初始化QEventLoop,因为我需要等待来自textWriter的信号。是否有其他方法可以暂停控制器并等待信号?我发现连接(&threadB,&QThread::finished,&threadB,&QThread::deleteLater)代码>导致错误的不稳定性:双重自由或腐败使用您的建议
QEventLoop eventloop;
connect(&workerStack, &Object::finish, &eventloop, &QEventLoop::quit);
threadStack.start();
threadHeap.start();
eventloop.exec();
threadStack.wait();