[SOLVED]Multithreading sync method

Started by
10 comments, last by the_edd 15 years, 9 months ago
Hi all. I'm kind of a newbie to multi-threading. I've been reading tons of literature and started experimenting with it. I'm concerned though that the following code may turn into unexpected behaviour (read it to the end, I'm AWARE of the existence of mutexes and critical sections) this is a simplified version of my code ;):
[source=C]
bool MyVar = false;

void thread01()
{
   //Do something...
   MyVar = true;
}

void thread02()
{
   //Do something...
   MyVar = true;
}

void thread03()
{
   //Do something...
   MyVar = true;
}

void thread04()
{
   while( !MyVar );
   //Do something else after MyVar==true
}


Like you can see, the intention is that thread04 would wait for the other threads until MyVar is true. When MyVar = true, all threads can stop. Thread04 has read-only capabilities over MyVar, and thread01-03 will read/write to MyVar. My approach ought to work because eventually MyVar will be true in thread04. But I'm concerned that MyVar remains always false due to cache incoherency:
[source=c]
while( !MyVar ); //Always false, even if it is not


But using mutexes & critical sections should have the same problem, unless the releasing of them invalidates the cache somehow:
[source=c]
void thread01()
{
   Lock();
   //Do something...
   MyVar = true;
   UnLock();
}

void thread02()
{
   Lock();
   //Do something...
   MyVar = true;
   UnLock();
}

void thread03()
{
   Lock();
   //Do something...
   MyVar = true;
   UnLock();
}

void thread04()
{
   Lock();
   while( !MyVar );
   UnLock();
}


Should I be concerned by the cache thing? AFAIK, the existing syncronization mechanisms should be used if one thread tried to set MyVar to false, while another one tried to true. Which is not the case. Another use is to avoid reading data that is temporary in an inconsistent state, which isn't also the case. Would this approach work? Or am I doomed? Thanks Cheers Dark Sylinc [Edited by - Matias Goldberg on July 13, 2008 12:35:33 PM]
Advertisement
Use:
volatile bool MyVar;

This will force the compiler to always read the variable from memory. If you are running this only on one machine all the cores on the motherboard should handle invalidating eachother's cache in hardware so you don't have to worry about that side of things. But if you don't specify volatile, the compiler is free to optimize the variable away to a constant, or just keep it in a register forever and thus never check that another thread modified it.
Thanks for the quick reply!
I was thinking about using volatile, but haven't tried yet.

I've just compiled and run the code and seems that it is working, otherwise it would be caught in an infinite loop. However I can't always assume this behaviour.
(Besides, I still need to check whether my output results are correct.)

Thanks!
Dark Sylinc
You really do need to be using synchronisation primitives (such as mutexes) to control acces to your myVar boolean anyway. I'm not sure that reading/writing to a boolean is guaranteed to be atomic.

But your method of locking/unlocking won't work - your threads will never terminate. thread4 locks the MyVar variable, then proceeds to infinite loop checking to see if it changed. Since it's locked none of the other threads can modify it, and you'll get deadlock.
NextWar: The Quest for Earth available now for Windows Phone 7.
It would probably be better to use a semaphore instead of a mutex like so:

void thread01()
{
//Do something...
SignalSemaphore();
}

void thread02()
{
//Do something...
SignalSemaphore();
}

void thread03()
{
//Do something...
SignalSemaphore();
}

void thread04()
{
WaitOnSemaphore();
}

Your mutex idea wouldn't work because you are locking out the other threads from doing anything (they will stall out on the lock) and you will have a deadlock.

Also, if you want to stick with MyVar, use volatile like KulSeran mentioned, and for the love of god use a yield function:

while( !MyVar ) Yield();

Otherwise thread04 will spend its entire timeslice spinning in a loop when other threads could be running.
If you need to wait for thread to complete, but do not care about accuracy, merely a signal (one thread waits for another), then volatile, when compiled on modern compilers, should be enough.

If you have multiple threads, with one thread waiting on one/many of them, using semaphore is recommended.

Under Windows, you have WaitFor***Object(s) that does this.


However: waiting and threads are generally counter-productive. Optimal way is to use asynchronous approach, when a thread completes, it fires a callback or event, which does the next part of code.

Pseudo-code:
void foo() {  // do lots of foo  async(bar());}void bar() {  // do just some of bar  async(foo());};


Here you end up with infinite loop, whenever one function is complete, it will call next function asynchronously (via message, or some other inter-thread communication mechanism).
Quote:Original post by Sc4Freak
I'm not sure that reading/writing to a boolean is guaranteed to be atomic.


If it declared volatile using visual studio then it is.
Quote:
void thread04(){   Lock();   while( !MyVar );   UnLock();}


This is what I would call dead lock
Thanks for all the replies!

I'll be trying the semaphore.
The only thing, is that all the other threads also check if the others have finished with success..

In single core, the idea is the following:
[source=C]for( i=0; i<100000; i++ ){  bool r = DoSomething();  if( r )     return true;}return false;


I'm splitting DoSomething() across all threads, but if one is found to be true, then all threads can stop (except the main one, which is waiting for the result)

Quote:Original post by NickW
Also, if you want to stick with MyVar, use volatile like KulSeran mentioned, and for the love of god use a yield function:

while( !MyVar ) Yield();

Otherwise thread04 will spend its entire timeslice spinning in a loop when other threads could be running.

Yeah, I know that, I'm using Sleep(1) for now. The code posted was to show the idea.

Thanks all
Dark Sylinc

Edit: Oh I just ended up in msdn. That's exactly what I want to do. Thanks for the guidance!

[Edited by - Matias Goldberg on July 13, 2008 12:26:11 PM]
Another thing worth mentioning, if you need to spawn a thread, then wait until it's complete (boost example from dr. Dobb's):
void hello(){  std::cout <<    "Hello world, I'm a thread!"    << std::endl;}int main(int argc, char* argv[]){  boost::thread thrd(&hello);  thrd.join();  return 0;}


A new thread is spawned, and main will wait until that one completes. If you know that threads will complete (you need guarantee for this), then you simply create all threads, and then join() them all.

This approach doesn't allow re-use of threads.

This topic is closed to new replies.

Advertisement