Share data accross threads and emit signals
Hi,
I need to have access to several information from different threads so I made a class like this:
Code:
class Foo
{
public:
void increase()
{
m_ints << 42;
}
private:
QVector<int> m_ints;
};
Foo is then instantiated in the main thread and a pointer is passed to the relevant threads. So far so good. Now I'd like to get informed if e.g. m_ints gets changed. So QObject came into play but as it is only reentrant (except connect and disconnect) it is getting complicated.
Let's think of this modified class:
Code:
{
public:
void increase()
{
m_ints << 42;
emit added(42);
}
signals:
void added(int);
private:
QVector<int> m_ints;
};
So, Foo's thread affinity is my main thread.
- Is this class still thread-safe since it does not use any functions of QObject? What about emit?
- What does this mean for calling increase() through a pointer from a different tread. Which thread does the "work" (manipulating m_ints) of increase(): The main thread or the local thread?
If that solution is bad, what would be a good approach to get informed about changes? (Beside the caller of the function emits afterwards the changes ala
Code:
myFoo->increase();
emit mySignalAdded(42);
Thanks
Lykurg
Re: Share data accross threads and emit signals
Quote:
Originally Posted by
Lykurg
Is this class still thread-safe since it does not use any functions of QObject? What about emit?
That should be ok.
Quote:
Originally Posted by
Lykurg
What does this mean for calling increase() through a pointer from a different tread. Which thread does the "work" (manipulating m_ints) of increase(): The main thread or the local thread?
The thread calling the method is the one executing its code.
If the receiver of the signal has a different thread affinity than the thread executing the emit, then the slot will be executed by the receiver's owner thread (assuming Qt::AutoConnection).
However, I would suggest to unlock before emitting, otherwise it is easy to get into a recursive lock situation (e.g. the slot or a methods called from the slot calling a method of this object again).
Cheers,
_
Re: Share data accross threads and emit signals
An alternative approach would be to use has-a instead of is-a approach, similar to what QFutureWatcher does. You can register an external QObject with your class, keeping your class thread-safe with the use of the lock and make the object inform the watcher (through e.g. QCoreApplication::postEvent()) that its content has changed (or whatever else you need). You may also look into lock-free structures if your vector only grows at the end to completely avoid the need for a lock.
Re: Share data accross threads and emit signals
Quote:
Originally Posted by
anda_skoa
The thread calling the method is the one executing its code.
If the receiver of the signal has a different thread affinity than the thread executing the emit, then the slot will be executed by the receiver's owner thread (assuming Qt::AutoConnection).
Not sure if I get you right (second sentence): Suppose I have two threads A and B beside my main thread MT.
Code:
// in MT
Foo *f = new Foo;
objInThreadA->setFoo(f);
objInThreadB->setFoo(f);
connect(foo, &Foo::added, objInThreadB, &Bar::doSomething); // autoConnection will use queuedConnection
// in Thread A
f->increase();
So who does what? My understanding is: ThreadA does the work in increase() and then ThreadB do with "doSomething" whatever it likes in his thread.
Quote:
Originally Posted by
anda_skoa
However, I would suggest to unlock before emitting, otherwise it is easy to get into a recursive lock situation (e.g. the slot or a methods called from the slot calling a method of this object again).
Yes, recursive lock is a risk but is emit thread safe? Doesn't it involve QMetaObject etc. With lifting the lock before emitting, wouldn't that open the possibility that two threads run in the same emit simultaneously?
Added after 4 minutes:
Quote:
Originally Posted by
wysota
An alternative approach would be to use has-a instead of is-a approach, similar to what
QFutureWatcher does. You can register an external
QObject with your class, keeping your class thread-safe with the use of the lock and make the object inform the watcher (through e.g.
QCoreApplication::postEvent()) that its content has changed (or whatever else you need).
Okay, have to think about that. Thanks for the tip.
Quote:
Originally Posted by
wysota
You may also look into lock-free structures if your vector only grows at the end to completely avoid the need for a lock.
Sure, that's the second step but the deadline leaves me no time to do that (at least for now)...
P.s.: Thanks to you, too, anda_skoa! Forget that in my previous reply.
Re: Share data accross threads and emit signals
Quote:
Originally Posted by
Lykurg
Not sure if I get you right (second sentence): Suppose I have two threads A and B beside my main thread MT.
Code:
// in MT
Foo *f = new Foo;
objInThreadA->setFoo(f);
objInThreadB->setFoo(f);
connect(foo, &Foo::added, objInThreadB, &Bar::doSomething); // autoConnection will use queuedConnection
// in Thread A
f->increase();
So who does what? My understanding is: ThreadA does the work in increase() and then ThreadB do with "doSomething" whatever it likes in his thread.
Yes, correct.
Quote:
Originally Posted by
Lykurg
Yes, recursive lock is a risk but is emit thread safe? Doesn't it involve
QMetaObject etc. With lifting the lock before emitting, wouldn't that open the possibility that two threads run in the same emit simultaneously?
I don't think emitting a signal changes anything inside the object, so that should be OK.
Cheers,
_
Re: Share data accross threads and emit signals
Not that I do not trust you :p but I wanted to be sure, so in case anyone else stumbles across this thread: emit is thread safe. It calls QMetaObject::activate which then uses a mutex.
Detailed information can be found at the end of this article: http://woboq.com/blog/how-qt-signals-slots-work.html
Thanks!