I wanted to confirm my understanding of threads and passing by reference in C++. Is the following function thread safe?
QString sA = "hello";
QString sB = "world";
bool someFlag = AreStringsEqual(sA,sB);
...
bool AreStringsEqual(QString const &stringA, QString const &stringB)
{
if(stringA == stringB)
{ return true; }
return false;
}
I think it is thread safe. I'd like it if someone could confirm my thought process, or tell me I have no idea what I'm talking about :)
There are two copies of sA and sB in the process's memory. One set is created on Thread1's stack and the second set is created on Thread2's stack. Because we passed by reference, each thread only needs one set of sA and sB in memory to execute the function call.
If we had passed by value instead, there could be up to four copies of sA and sB in the process's memory (each thread having two sets) at some time point where both threads were trading processor control within the function call.
In no case is memory shared here, therefore the function is thread safe.
Sorry if this question is super simple, threads have fried my brain :)
Pris
There's no reason why two threads wouldn't hold references to the same strings.
This function is not thread-safe because the statement if(stringA == stringB)
is not atomic.
First you fetch stringA
from memory, and only then string B
.
Let's stay stringA == stringB == 2
.
You fetch stringA
, then there's a context switch and both stringA and stringB change to 3. Then you fetch stringB
. Your function would return false (because 2 != 3
) although stringA
was equal to stringB
all along.
Your question is a little vague on where sA
and sB
are declared. It sounds like they are declared inside a function, in which case you're correct that each thread would have it's own version of sA
and sB
. But, in the odd chance that they are declared at global scope, this is not the case. If I understand your question correctly, you meant that the two were declared at local scope, so your first point is correct. By the same token, your second point is correct as well.
Your third point is tricky, though. In your particular case, no memory is shared, so your program is a "thread-safe" program (not sure if that's a good way to word it). However, the function AreStringsEqual
is not thread-safe. At some point in the future, you (or someone else) could use the function with data that is shared, and the function itself does not guard itself against this usage.
Unless QString
has specified that operator==
is thread safe, the function is not thread safe. The implementation of AreStringsEqual
does nothing itself to protect the data.
You are putting the responsibility of thread safety on the client with this implementation. The client must ensure the parameters and the parameters' internal data does not mutate (e.g. by another thread) while in AreStringsEqual
. Consequently, they may find themselves making unnecessary copies. How exactly this must happen is dictated by the implementation of QString
. Even std::string
implementations vary dramatically =)
For strings in concurrent contexts, one would generally take a copy before moving the string into a concurrent context. If it really needs to be shared, you'll need something to protect it (such as a lock). For primitive collections (e.g. std::string
and std::vector
), you'll want to avoid locking at every access because it would kill performance and could fail rather easily. So, you'd generally copy or lock if you must share objects which are not explicitly thread safe.
Therefore, the implementation of AreStringsEqual
is not thread safe (again, unless bool QString::operator==(const QString&) const
is guaranteed to be thread safe).
However, your usage of AreStringsEqual
:
QString sA = "hello";
QString sB = "world";
bool someFlag = AreStringsEqual(sA,sB);
would be fine for the majority of string implementations, because the parameters and their data would be local to the thread.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With