我在Qt5Application中有2个线程:
线程A:包含一堆QObject派生类对象
线程B:此线程中的worker具有指向A中对象的所有指针
线程A有时可能非常繁忙,而线程B只在那里委托信号并管理其他一些东西。它永远不会写入任何这些对象,但我需要检查一些从A中的对象返回布尔值的getter函数。
in ThreadB:
if (objInThrA->isFinished()) { ... }
isFinished()返回一个布尔值。
如果一个函数中的线程A真的很忙,我在线程B中调用了这些isFinished函数,那么我的线程B会在线程A完成其工作之前停止运转,还是会有效?
答案 0 :(得分:4)
可以在不同线程之间使用Qt信号和插槽。但有两条规则:
connect()
的最后一个参数应为Qt::QueuedConection
或Qt::BlockingQueuedConnection
(或默认为Qt::AutoConnection
,如果对象与Qt::QueuedConnection
相同属于不同的线程)。 QueuedConnection
表示发射器不等待信号处理完成,BlockingQueuedConnection
表示 。
QObject派生类不适合在线程之间传递。在此之前应该安全地复制它们(请参阅QMetaType文档)。
答案 1 :(得分:1)
您永远不应该访问成员或直接调用另一个线程中的对象的函数。唯一安全的方法是通过信号/插槽机制访问它们。您可以在ThreadB
中发出信号并将其连接到ThreadA
中的一个位置,该位置返回值:
connect(objInThrB, SIGNAL(getFinished()), objInThrA, SLOT(isFinished()), Qt::BlockingQueuedConnection);
这种方式在线程B中发出信号时如下:
bool ret = getFinished();
当控制返回到线程A的事件循环并且返回值时,将调用线程A中的槽。线程B等待调用插槽,因为连接类型是BlockingQueuedConnection
。所以请注意不要使用这种阻塞连接来阻止应用程序主线程。
答案 2 :(得分:0)
好的,我自己测试了一下。
在主题B中:
connect(this,SIGNAL(runWork()),objInThrA,SLOT(doWork()));
emit runWork();
QThread::sleep(2);
qDebug() << objInThrA->isFinished();
线程A中的:
qDebug() << "start A sleep";
QThread::sleep(10);
qDebug() << "end A sleep";
<强>输出:强>
start A sleep
false
end A sleep
它有效,但是我仍然不确定我是否以这种方式使用它正确完成和定义的行为。
答案 3 :(得分:0)
简短的回答是否。
当你按照描述运行时,你直接从一个不在你线程中的对象调用方法(该对象在线程A中,但你从线程B调用它的一个方法)。直接调用方法不是Qt从标准C ++修改过的,因此它不会通过信号,插槽或事件循环进行操作,并且不了解您的线程模型。这意味着如果在线程B中调用该方法,它将在线程B中运行。
如果你不小心它,那么从另一个线程调用对象的方法可能是危险的,因为它引入了并发问题。如果线程A正在更新_mFinished数据成员,而线程B在其上调用getFinished(),则它可能会获得部分写入的值。在您的具体示例中,恰好是没有&#34;部分写入&#34;布尔的状态,你可能会没事。
解决并发问题是通过对多个线程之间共享的元素进行原子操作来完成的。您可以使用保证是原子读取和写入操作的变量,例如布尔值,或者您可以使用锁定机制来保护这些变量。互斥锁和信号量是最常见的锁定机制,允许从多个线程锁定,以限制在读取和写入变量时对变量的访问。这样做是为了避免在读取变量时用新值部分写入它们
如果您正在从事涉及多个线程的工作,我建议您阅读互斥锁和信号量(通用多线程数据结构),因为它们对于理解线程通常至关重要。另外Qt有很好的Qt包装版本,使它们易于使用,并避免在维护其使用时出现一些容易的陷阱(例如QMutexLocker)。