0

I have four threads that need to be syncronized. In order to achieve this, I am looking to pass each thread the same bool array. Each thread will change the 'corresponding' value to true once it has reached a certain point in the thread, then constantly check to see if the rest of the values are true. If so, enter the loop... An example

//above thread init stuff
oLock->lockForWrite();
abSync[iThreadNum] = true;  //iThreadNum = {0...3} depending on whats set
oLock->unlock();

bool bSynced = false;
while (!bSynced)
{
    oLock->lockForRead();
    if (abSync[0] && abSync[1] && abSync[2] && abSync[3])
        bSynced = true;
    oLock->unlock();
}

//below thread run and finish

Does the QReadWriteLock work as described above? Will it in fact lock the variable for the write (as each thread goes through) but not for the read? A quick look at the documentation suggests that QReadWriteLock will only block at lockForRead() if there is a write lock but not a read lock which is what I want above.

Also, I understand the above will suck down CPU cycles as its going through the loop and that is the wanted behavior. Using a Sleep is not good enough for our needs.

Community
  • 1
  • 1
g19fanatic
  • 10,567
  • 6
  • 33
  • 63
  • Why do you need a lock if each thread has only one piece of the array it can change? What is sleep not good enough for? What are you trying to do? You won't be able to get better precision than sleep. Even if you write assembly, the OS is free to do whatever the hell it wants at any time with your code, ESPECIALLY on windows. – Falmarri Dec 14 '10 at 18:54
  • I need a lock because each thread must start execution at the same time. Each thread can only change 1 part, but reads all 4 parts... Each thread is handling some communication to 4 separate pieces of hardware and so long as they start at the same time everything is as it should be. And you can get MUCH better precision that sleep if you do something like the following: http://stackoverflow.com/questions/4233726/limit-iterations-per-time-unit/4233821#4233821 – g19fanatic Dec 14 '10 at 18:58
  • how about using a QWaitCondition to avoid the busy-wait? – Frank Osterfeld Dec 14 '10 at 20:15

3 Answers3

3

No the QReadWriteLock doesn't do exactly what you want to do, after reading your comment above you really dont need any lock for writting data, you just need to assure that the reads are volative on your array. If you need the threads to run at the same time why not a count down latch? That ensures that all threads (possibly) start at the same time.

DVD
  • 1,744
  • 3
  • 17
  • 34
  • isn't what I'm doing above equilivant to a count down latch? Qt doesn't have an implementation of a countdownlatch so this was my attempt at making one while using an object I haven't used in the past `QReadWriteLock`. – g19fanatic Dec 14 '10 at 19:27
  • Yes it's something like that but your array must be volatile or the compiler can replace the reads of it with simples registers compares. Your lock isn't needed but that code is very inclined to future erros. If it was me to do that i would use monitors to make that syncronization – DVD Dec 14 '10 at 19:34
  • 1
    In the end, I re-wrote my syncing section to incorporate a count down latch similar sync method. I used Qt's QAtomicInt, which performs inc and dec atomically thus enabling multi-threaded apps the capability of using it. Much more simple than the method I was using. Thanks – g19fanatic Dec 17 '10 at 12:13
2

I don't know what your margin is for "running at the same time", but threads don't run at the same time and it is hard to predict what the offsets will be. What you try to achieve is a threading barrier, which should be done with condition variables (QWaitCondition), but even that doesn't guarantee starting at the same time, just that they won't start before all have finished previous work (but that can be alright is your margin is big enough).

stefaanv
  • 14,072
  • 2
  • 31
  • 53
1

I presume the requirement here is something like:

// each thread intends to do this
doStep1();
makeSureEveryOtherThreadHasDoneStep1();
doStep2();

I also presume it doesn't really matter whether all doStep2()s "run at the same time" or not, as long as each of the doStep2()s run after all the doStep1()s have completed.

Seen with those two presumptions, the code posted in the question looks correct, but uses a busy wait. A better solution would be to use QWaitCondition with a QMutex. Something like:

doStep1();
mutex->lock();
abSync[iThreadNum] = true;
if (abSync[0] && abSync[1] && abSync[2] && abSync[3]) {
    waitCondition->wakeAll(); // yay! all are in sync
} else {
    waitCondition->wait(mutex);
}
mutex->unlock();
doStep2();

Edited: mutex->unlock() moved from just after wakeAll() to just before doStep2() per comments below, thanks to @stefaanv.

roop
  • 1,291
  • 8
  • 10
  • your mutex-unlock() must happen just before doStep2, otherwise, only 1 waiter can wake up (mutex retaken). Otherwise, this is pretty much what I meant. – stefaanv Dec 15 '10 at 13:47
  • almost, each thread is properly timed to execute completely independant of another one. I know this work because when I do not do any syncronization, i get the expected output that some threads have more 'data' than others because they ran longer. My timing requirements are in the sub-milliseconds per iteration per loop run, meaning the threshold for all of the threads to start at the same time is roughly .3-.5 ms. Using a busy wait is acceptable for my application as I do not want to leave it up to the OS' scheduler to start the threads – g19fanatic Dec 15 '10 at 13:49
  • @g19fanatic: you did your metrics, so empirically, there is no problem, but it is still your OS that does the scheduling. It decides which thread is running. You're even slowing down your system because the last thread will only run when it gets a timeslice while the other threads are using up their timeslices. – stefaanv Dec 15 '10 at 14:14
  • @stefaanv, is this true ("...will only run when it gets a timeslice while the other threads...") for an 8 core system? (not 4 w/ HT, but actual 8 cores, 2 cpu, quad core processor machine). Taking a quick look at my system resource use shows that I am in fact sucking down 4 CPUs at 100% due to my thread timing (take a look at the link i posted in my comments on how i'm doing my software timing). I'm not running this on a single core machine and expecting threading to handle it, if that is an unanswered assumption – g19fanatic Dec 15 '10 at 14:24
  • @g19fanatic: as I said, you did your metrics. You didn't mention multi-core, so I didn't have to assume it. It is still the OS that schedules, but if your metrics show that you use 4 CPU's then you're okay. Mind you that the mutexes still have to do some communication over the cores to know whether it is free. – stefaanv Dec 15 '10 at 14:32
  • @g19fanatic ("the threshold for all of the threads to start at the same time is roughly .3-.5 ms") so my second presumption is not true. In that case, yes, you dont want to use the OS scheduler, and your busy wait is the way to go. However, my suggestion would be to restructure your threads such that step1 ans step2 are in different threads. When all step1 threads have finished, you can start all step2 threads at one shot. – roop Dec 16 '10 at 05:23
  • @stefaanv ("otherwise, only 1 waiter can wake up (mutex retaken)") wait() unlocks the mutex, so at the time of wakeup, only the last thread holds the mutex, which it unlocks. So I dont see why unlock() should move down to after the else block. Did I miss something? – roop Dec 16 '10 at 05:25
  • @roop: wait() has to unlock so others can use the lock to change the condition, but on wakeup, before leaving wait(), it has to take back the lock to check the condition (standard use of condition variables), so the lock must always be unlocked after wait. Try it by printing in doStep2(). – stefaanv Dec 16 '10 at 08:53
  • @stefaanv Agree. qwaitcondition code does seem to do that. I've edited my answer. Thanks. – roop Dec 17 '10 at 14:19