I'm pondering a question, and have made a harness to do some testing, and would like someone who is smarter than me to straighten my head out.
Here's the important section of code:
1 2 3 4 5 6 7 8 9
|
long GetNextNumber() {
long retVal = 0;
myMutex.lock();
if (currentNumber == LONG_MAX)
currentNumber = 0;
retVal = ++currentNumber;
myMutex.unlock();
return retVal;
}
|
With lines 3 and 7 commented out, I can have 2 threads get the same number from this method. With the code as-is above, I can't get the same number from this method, at least in my testing.
Scenario: Thread A running through line 7 then Thread B running all lines, then Thread A running line 8.
Questions:
Is there something stopping this from happening? Or perhaps I haven't run enough cycles to get this (probably rare) scenario to happen?
If this scenario can happen, how would you prevent it?
Here's my full code in case you want to play. I'm running this in Fedora 17 in a VMWare virtual machine. The scenario hasn't happened (yet) in that environment.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
|
/*
* main.cpp
*
* Created on: Aug 10, 2012
* Author: jmjatlanta
*/
#include <thread>
#include <iostream>
#include <chrono>
#include <mutex>
#include "limits.h"
using namespace std;
class NumberQueue {
private:
long currentNumber;
mutex myMutex;
public:
NumberQueue() { currentNumber = 0;}
~NumberQueue() {}
long GetNextNumber() {
long retVal = 0;
//myMutex.lock();
if (currentNumber == LONG_MAX)
currentNumber = 0;
retVal = ++currentNumber;
//myMutex.unlock();
return retVal;
}
};
class NumberGrabber {
private:
shared_ptr<NumberQueue> numberQueue;
public:
shared_ptr<NumberGrabber> Sibling;
long MyNumber;
bool Run;
NumberGrabber(shared_ptr<NumberQueue> queue) : MyNumber(-1), Run(true), numberQueue(queue) { }
~NumberGrabber() {}
void RunOnMyOwn() {
while(Run) {
MyNumber = (*numberQueue).GetNextNumber();
long theirNumber = (*Sibling).MyNumber;
if (MyNumber == theirNumber) {
cout << "My Number [" << MyNumber << "] is the same as my sibling [" << theirNumber << "]" << endl;
}
}
}
};
void runThread(shared_ptr<NumberGrabber> grabber) {
(*grabber).RunOnMyOwn();
}
int main(int argc, char* argv[]) {
shared_ptr<NumberQueue> numberQueue(new NumberQueue());
shared_ptr<NumberGrabber> grabber1(new NumberGrabber(numberQueue));
shared_ptr<NumberGrabber> grabber2(new NumberGrabber(numberQueue));
(*grabber1).Sibling = grabber2;
(*grabber2).Sibling = grabber1;
cout << "Firing up thread 1" << endl;
thread t1(runThread, grabber1);
cout << "Firing up thread 2" << endl;
thread t2(runThread, grabber2);
cout << "Pausing main thread by spinning" << endl;
for(long l = 0; l < LONG_MAX; l++) {
// something that takes time
this_thread::sleep_for(chrono::milliseconds(100));
}
(*grabber1).Run = false;
(*grabber2).Run = false;
cout << "About to join thread 2" << endl;
t2.join();
cout << "About to join thread 1" << endl;
t1.join();
cout << "Ending the program" << endl;
return 0;
}
|