Solved Make QNetworkAccessManager threadsafe
-
Hello everyone,
in my application, I'm using a QNetworkAccessManager (cached and with a QNetworkCookieJar).
The documentation mentions that QNetworkAccessManager is just reentrant, not threadsafe.
This SO post also says that if using multiple threads, one should use one instance per thread.Question 1: Is this correct? It seems a bit counter-intuitive that I have to create multiple instances of a QNetworkAccessManager for multi-threaded applications, although the documentation also mentions that "One QNetworkAccessManager should be enough for the whole Qt application."
If the QNetworkAccessManager is indeed just reentrant, my plan is to work around this limitation by running just one QNetworkAccessManager in a dedicated thread, which is managed by a threadsafe object. Requests would then only be sent through this threadsafe object.
Question 2: Is this feasible? Is there an easier solution? My only requirements are that I want to have only one instance of the QNetworkAccessManager and still use it from different threads.
Cheers
PhoeNox -
Hi,
maybe you should keep a one and only net work manager in the main thread and do the work initiated by the requests it gets in separate threads.
-Michael. -
@Phoenox ...
Typically "re-entrant" implies thread-safe as variables are on the stack and do not interfere with each other. That being said, if there are static methods being called using static members than that can cause problems.If you are worried, use your own mutexes around your access points into QNetworkAccessManager then you make your own thread-safety for that instance.
-
@Phoenox said in Make QNetworkAccessManager threadsafe:
Question 1: Is this correct?
Yes, that's correct. NAM uses threading internally though.
Question 2: Is this feasible?
It's a good plan. However you can just use plain ol' signal and slots instead of using your own thread-safe wrapper around the (single) NAM instance.
@Buckwheat said in Make QNetworkAccessManager threadsafe:
Typically "re-entrant" implies thread-safe as variables are on the stack and do not interfere with each other.
Not exactly. Reentrant literally means a thread can be interrupted and then the reentry can be made without side effects. This implies that the variables are thread-local (independent of stack or heap), i.e. there are no globals to take care of.
-
@kshegunov ... correct. but add... there are no side-effects if invoked again before the last invocation finishes. Hence no static (global) members. The safest way is still to use mutexing and protect yourself.
-
@Buckwheat said in Make QNetworkAccessManager threadsafe:
... correct. but add...
No "but". It means safe for re-entry. It doesn't include "if invoked again". A global may be modified from anywhere, and a thread context switch can happen (almost) anywhere, so there's no reason to think that there's need for a condition to be put to the function to be called a second time.
-
@kshegunov ...
https://en.wikipedia.org/wiki/Reentrancy_(computing)
Reentrancy (computing) In computing, a computer program or subroutine is called reentrant if it can be interrupted in the middle of its execution, and then be safely called again ("re-entered") before its previous invocations complete execution.
That is the definition I grew up in computing when concurrency was a nifty trick and threads were not part of the OS. So, both are correct. I had already mentioned the exception of globals. So, no need to be angry and snotty.
-
So, no need to be angry and snotty.
Not intended as such, sorry if it was perceived this way. I meant the following (real code):
int testFunction() { int x = 0; return x + 5; }
leading to:
11 [1] { 0x5555555568f0 55 push %rbp 0x5555555568f1 <+0x0001> 48 89 e5 mov %rsp,%rbp 12 [1] int x = 0; 0x5555555568f4 <+0x0004> c7 45 fc 00 00 00 00 movl $0x0,-0x4(%rbp) 13 [1] return x + 5; 0x5555555568fb <+0x000b> 8b 45 fc mov -0x4(%rbp),%eax 0x5555555568fe <+0x000e> 83 c0 05 add $0x5,%eax 14 [1] } 0x555555556901 <+0x0011> 5d pop %rbp 0x555555556902 <+0x0012> c3 retq
This function can be thought reentrant if a thread breaks after the
movl $0x0,-0x4(%rbp)
and when re-entering it can executemov -0x4(%rbp),%eax
without side effects, which is the implication that another thread can't (or will not) modify-0x4(%rbp)
while the former is sleeping. And since one can't control where context switches are done, we get the implication that globals break reentrancy. So that's what I meant by It doesn't include "if invoked again".Kind regards.
-
@kshegunov ... I said globals are bad: "That being said, if there are static methods being called using static members than that can cause problems." I hate to date myself, but I was writing machine guidance and avionics before you were actually born! LOL!
-
@Buckwheat said in Make QNetworkAccessManager threadsafe:
I said globals are bad
Never claimed you hadn't. I was just expanding on my comment, making it clearer what I meant, or at least I hope.
-
@kshegunov ... I said globals are bad: "That being said, if there are static methods being called using static members than that can cause problems."
One more thing. Your example is always re-entrant and always thread-safe no matter what circumstances because it is all stack/frame pointer based. So it proves the point I was making. Only in languages like FORTRAN where all variables are static would there be a problem. But you can also make some really cool design strategies with it.
-
@kshegunov ... OK. It is hard to have tech discussions like this. A large cup of coffee and a nice Xtreme session would be ideal :D
-
Okay, I just protected my single instance of the QNetworkAccessManager with mutexes, as @Buckwheat suggested.
Until now, I'm not experiencing any problems.Thanks everyone for the tips and the clarification on reentrancy/thread-safety.