Reputation: 61
I have the following code. Is there anything that would make it non-threadsafe?
class runner
{
public:
volatile int exitFlag;
// construct in thread A
runner()
{
exitFlag = 0;
}
// run it in thread B
void threadFunc()
{
// does not matter when the change is getting here
while (exitFlag == 0)
{
// ... do stuff (not using exitFlag)
}
}
// call it from thread A
void signalThread()
{
exitFlag = 1; // only change one bit;
}
};
I create a runner
object and start it's threadFunc
in another thread and some time later call signalThread
on it from the first thread.
I was told that accessing the same variable in different threads (at least of them is write) could lead to reading garbage value. But obviously in the above code (since only one bit is changed) it doesn't matter.
Also the order of read/write is not matter.
Upvotes: 4
Views: 2282
Reputation:
To clarify what has been said, although slightly buried it would seem since you're not saying you understand:
When the value is changed on one CPU it will mostly/usually only get written to the cache of that CPU. Other CPU's will not see it - other threads should NOT be working with exactly the same memory without applying synchronisation techniques!
This changed word of memory may eventually be flushed out to the main memory, but how long that takes is totally unpredictable. Also, if other CPU's think they have that same word of memory in their own cache, they may not go looking for another copy until such times as they discard that piece of memory and later fetch it again.
Mutexes use 'memory barriers' which are basically mechanisms in the CPU and sub-system which allows a thread to say "I want to see TRUE values of all data changed before and under the influence of this mutex. Thus, when one thread acquires a lock on a mutex, modifies a piece of memory, and releases the mutex, ANY other thread that acquires that mutex will be guaranteed to see all memory changed under the guardianship of that mutex.
You MUST use mutexes or other sync objects in a multithreaded system, ESPECIALLY now in this age of multi-core CPU's. Back in the single-core days, you would have gotten away with what you are proposing; the only risk back then would have been broken atomicity. Even a simple machine word can suffer broken read/modify/write cycles on a single core machine, let alone multi-core...
Upvotes: 0
Reputation: 61
To sum up the comments (mostly from Mike Seymour)
std::atomic
when availableThe posted code contains undefined behaviour (stated by c++ standard) but the only specific problem that could arise is thread B not getting the changes in reasonable time because they are stuck in cpu chache.
Upvotes: -6
Reputation: 254461
Don't use volatile
for sharing variables between threads. It does not give suitable guarantees of atomicity or synchronisation. Lack of atomicity is unlikely to cause specific problems here, although formally it causes undefined behaviour. Lack of synchronisation means that it's entirely possible for the thread never to see the change, and carry on running forever.
In C++11 or later, use std::atomic<int>
. Earlier versions of the language had no standard support for threads, so you'll have to use whatever non-standard facilities your compiler provides.
Upvotes: 9
Reputation: 9863
The simple fact that the data member is not guarded (e.g. by a mutex) makes the code non-threadsafe. Several threads can access the same instance of runner
(e.g. via a pointer or a reference), and could access exitflag
simultaneously, since no synchronization mechanism is in place.
To answer the question in your comment: Since int
is defined as the CPU's "native" type, I believe that a given read or write operation is a single machine code instruction and will not be interrupted by another thread. I don't know whether C/C++ will that guarantee that behavior, though. So in your scenario, it will probably work, although in the general case you need synchronization.
Upvotes: 0