I have 2 Threads in a Qt5Application:
我有两个线程在Qt5Application:
Thread A: contains a bunch of QObject derived class objects
线程A:包含一系列QObject派生类对象
Thread B: worker in this Thread has all the pointers to the objects in A
线程B:这个线程中的worker拥有所有指向A中的对象的指针
Thread A might be very busy at times and Thread B is only there to delegate Signals and manage some other stuff. It never writes to any of these objects, but I need to check some getter functions which return booleans from the objects in A.
线程A有时可能非常繁忙,而线程B只是在那里委托信号和管理其他东西。它从不对这些对象进行写入,但我需要检查一些getter函数,这些函数返回A中的对象的布尔值。
in ThreadB:
if (objInThrA->isFinished()) { ... }
The isFinished() returns a boolean.
isFinished()返回一个布尔值。
If Thread A is really busy in a function and I call these isFinished functions in Thread B, will my Thread B get stalled until Thread A is finished with its work, or will this work?
如果线程A在一个函数中真的很忙,而我在线程B中调用这些已完成的函数,那么线程B会在线程A完成其工作之前停止运行吗?
4 个解决方案
#1
3
Qt signals and slots can be used between different threads. But there are two rules:
Qt信号和插槽可以在不同的线程之间使用。但有两个规则:
-
Last argument of
connect()
should be eitherQt::QueuedConection
orQt::BlockingQueuedConnection
(or defaults toQt::AutoConnection
, which is the same asQt::QueuedConnection
if objects belong to different threads).QueuedConnection
means that emitter does not wait signal processing to be completed,BlockingQueuedConnection
means it does.connect()的最后一个参数应该是Qt: QueuedConection或Qt::BlockingQueuedConnection(或默认为Qt::AutoConnection,如果对象属于不同的线程,则与Qt::QueuedConnection相同)。QueuedConnection表示发送者不等待信号处理完成,BlockingQueuedConnection表示它已经完成。
-
QObject-derived classes are not suitable for passing between threads. They should be safely copied before that (see QMetaType docs).
qobject派生类不适合在线程之间传递。在此之前,应该安全地复制它们(参见QMetaType文档)。
#2
1
You should never access members or directly call functions of an object which in another thread. The only safe way is to access them through signal/slot mechanism. You can have a signal in ThreadB
and connect it to a slot in ThreadA
which returns the value:
您不应该访问成员或直接调用在另一个线程中的对象的函数。唯一安全的方法是通过信号/插槽机制访问它们。您可以在ThreadB中有一个信号,并将它连接到ThreadA中的一个槽,该槽返回值:
connect(objInThrB, SIGNAL(getFinished()), objInThrA, SLOT(isFinished()), Qt::BlockingQueuedConnection);
This way when you emit the signal in thread B like:
这样,当您在线程B中发出信号时,例如:
bool ret = getFinished();
the slot in thread A would be called when control returns to the event loop of thread A and the value would be returned. Thread B waits for the slot to be called, that's because the connection type is BlockingQueuedConnection
. So be aware not to block the application main thread using this kind of blocking connection.
当控件返回到线程A的事件循环并返回值时,将调用线程A中的槽。线程B等待要调用的插槽,这是因为连接类型是BlockingQueuedConnection。因此,注意不要使用这种阻塞连接阻塞应用程序主线程。
#3
0
Ok, so I tested it myself.
我自己测试过。
In Thread B:
在线程B:
connect(this,SIGNAL(runWork()),objInThrA,SLOT(doWork()));
emit runWork();
QThread::sleep(2);
qDebug() << objInThrA->isFinished();
in Thread A:
在线程:
qDebug() << "start A sleep";
QThread::sleep(10);
qDebug() << "end A sleep";
OUTPUT:
输出:
start A sleep
false
end A sleep
It works, however Im still unsure if I use it this way its correctly done and defined behavior.
它是有效的,但是我仍然不确定我是否用这种方法正确地完成和定义了行为。
#4
0
The short answer is no.
When you run as described, you're directly calling methods from an object which is not in your thread (the object is in Thread A but you're calling one of its methods from Thread B). Directly calling a method is not something Qt has modified from standard C++, so it doesn't operate through signals, slots, or the event loop and is ignorant of your threading model. That means that if you call the method in Thread B, it runs in Thread B.
简而言之,答案是否定的。运行描述时,你直接从一个对象调用方法并不是在你的线程(线程的对象但你调用一个方法从线程B)。直接调用一个方法不是Qt已经从标准c++修改,所以它不会通过信号槽,或事件循环,无知的你的线程模型。这意味着如果在线程B中调用该方法,它将在线程B中运行。
Calling methods on an object from another thread can be dangerous if you aren't careful about it because it introduces problems with concurrency. If Thread A is in the middle of updating the _mFinished data member when Thread B calls getFinished() on it, it may get a partially written value. In your specific example it happens to be that there is no "partially written" state for the boolean and you'll probably be fine.
Resolving concurrency problems is done through atomic operations on the elements which will be shared between multiple threads. Either you can use variables that are guaranteed to be atomic read and write operations, like booleans usually are, or you can use locking mechanisms to protect those variables. Mutexes and Semaphores are the most common locking mechanism and allow locking from multiple threads to limit access to the variables while they're being read and written. This is done in such a way as to avoid them being partially written with a new value when a read of the variable occurs.
I'd recommend reading up on mutexes and semaphores (generic multithreaded data structures) if you're doing work that involves more than one thread since they're critical to understanding threading in general. Additionally Qt has nice Qt wrapped versions of them that keep them simple to use and avoid some easy pitfalls in maintaining their usage (e.g. QMutexLocker).
如果您不小心的话,从另一个线程调用对象的方法可能是危险的,因为它会带来并发问题。如果线程A在更新_mFinished数据成员时,线程B调用它的getFinished(),它可能会得到一个部分写入的值。在您的特定示例中,正好没有布尔值的“部分写入”状态,所以您可能不会有问题。解决并发问题的方法是对将在多个线程之间共享的元素进行原子操作。您可以使用保证是原子读和写操作的变量,比如布尔值通常是这样的,也可以使用锁定机制来保护这些变量。互斥和信号量是最常见的锁定机制,允许多个线程的锁定限制在读取和写入变量时对变量的访问。这样做的目的是避免在读取变量时将其部分写入新值。如果您所做的工作涉及多个线程,那么我建议您阅读互斥体和信号量(通用的多线程数据结构),因为它们对理解线程的一般理解非常重要。此外,Qt也有很好的Qt封装版本,使它们易于使用,并且避免了在维护它们的使用时容易出现的一些缺陷(例如QMutexLocker)。
#1
3
Qt signals and slots can be used between different threads. But there are two rules:
Qt信号和插槽可以在不同的线程之间使用。但有两个规则:
-
Last argument of
connect()
should be eitherQt::QueuedConection
orQt::BlockingQueuedConnection
(or defaults toQt::AutoConnection
, which is the same asQt::QueuedConnection
if objects belong to different threads).QueuedConnection
means that emitter does not wait signal processing to be completed,BlockingQueuedConnection
means it does.connect()的最后一个参数应该是Qt: QueuedConection或Qt::BlockingQueuedConnection(或默认为Qt::AutoConnection,如果对象属于不同的线程,则与Qt::QueuedConnection相同)。QueuedConnection表示发送者不等待信号处理完成,BlockingQueuedConnection表示它已经完成。
-
QObject-derived classes are not suitable for passing between threads. They should be safely copied before that (see QMetaType docs).
qobject派生类不适合在线程之间传递。在此之前,应该安全地复制它们(参见QMetaType文档)。
#2
1
You should never access members or directly call functions of an object which in another thread. The only safe way is to access them through signal/slot mechanism. You can have a signal in ThreadB
and connect it to a slot in ThreadA
which returns the value:
您不应该访问成员或直接调用在另一个线程中的对象的函数。唯一安全的方法是通过信号/插槽机制访问它们。您可以在ThreadB中有一个信号,并将它连接到ThreadA中的一个槽,该槽返回值:
connect(objInThrB, SIGNAL(getFinished()), objInThrA, SLOT(isFinished()), Qt::BlockingQueuedConnection);
This way when you emit the signal in thread B like:
这样,当您在线程B中发出信号时,例如:
bool ret = getFinished();
the slot in thread A would be called when control returns to the event loop of thread A and the value would be returned. Thread B waits for the slot to be called, that's because the connection type is BlockingQueuedConnection
. So be aware not to block the application main thread using this kind of blocking connection.
当控件返回到线程A的事件循环并返回值时,将调用线程A中的槽。线程B等待要调用的插槽,这是因为连接类型是BlockingQueuedConnection。因此,注意不要使用这种阻塞连接阻塞应用程序主线程。
#3
0
Ok, so I tested it myself.
我自己测试过。
In Thread B:
在线程B:
connect(this,SIGNAL(runWork()),objInThrA,SLOT(doWork()));
emit runWork();
QThread::sleep(2);
qDebug() << objInThrA->isFinished();
in Thread A:
在线程:
qDebug() << "start A sleep";
QThread::sleep(10);
qDebug() << "end A sleep";
OUTPUT:
输出:
start A sleep
false
end A sleep
It works, however Im still unsure if I use it this way its correctly done and defined behavior.
它是有效的,但是我仍然不确定我是否用这种方法正确地完成和定义了行为。
#4
0
The short answer is no.
When you run as described, you're directly calling methods from an object which is not in your thread (the object is in Thread A but you're calling one of its methods from Thread B). Directly calling a method is not something Qt has modified from standard C++, so it doesn't operate through signals, slots, or the event loop and is ignorant of your threading model. That means that if you call the method in Thread B, it runs in Thread B.
简而言之,答案是否定的。运行描述时,你直接从一个对象调用方法并不是在你的线程(线程的对象但你调用一个方法从线程B)。直接调用一个方法不是Qt已经从标准c++修改,所以它不会通过信号槽,或事件循环,无知的你的线程模型。这意味着如果在线程B中调用该方法,它将在线程B中运行。
Calling methods on an object from another thread can be dangerous if you aren't careful about it because it introduces problems with concurrency. If Thread A is in the middle of updating the _mFinished data member when Thread B calls getFinished() on it, it may get a partially written value. In your specific example it happens to be that there is no "partially written" state for the boolean and you'll probably be fine.
Resolving concurrency problems is done through atomic operations on the elements which will be shared between multiple threads. Either you can use variables that are guaranteed to be atomic read and write operations, like booleans usually are, or you can use locking mechanisms to protect those variables. Mutexes and Semaphores are the most common locking mechanism and allow locking from multiple threads to limit access to the variables while they're being read and written. This is done in such a way as to avoid them being partially written with a new value when a read of the variable occurs.
I'd recommend reading up on mutexes and semaphores (generic multithreaded data structures) if you're doing work that involves more than one thread since they're critical to understanding threading in general. Additionally Qt has nice Qt wrapped versions of them that keep them simple to use and avoid some easy pitfalls in maintaining their usage (e.g. QMutexLocker).
如果您不小心的话,从另一个线程调用对象的方法可能是危险的,因为它会带来并发问题。如果线程A在更新_mFinished数据成员时,线程B调用它的getFinished(),它可能会得到一个部分写入的值。在您的特定示例中,正好没有布尔值的“部分写入”状态,所以您可能不会有问题。解决并发问题的方法是对将在多个线程之间共享的元素进行原子操作。您可以使用保证是原子读和写操作的变量,比如布尔值通常是这样的,也可以使用锁定机制来保护这些变量。互斥和信号量是最常见的锁定机制,允许多个线程的锁定限制在读取和写入变量时对变量的访问。这样做的目的是避免在读取变量时将其部分写入新值。如果您所做的工作涉及多个线程,那么我建议您阅读互斥体和信号量(通用的多线程数据结构),因为它们对理解线程的一般理解非常重要。此外,Qt也有很好的Qt封装版本,使它们易于使用,并且避免了在维护它们的使用时容易出现的一些缺陷(例如QMutexLocker)。