So, I’m trying to make a simple multi-threaded program that validates the Collatz Conjecture for a large set of numbers and returns the total amount of validated numbers. Each thread (total 4) does an interval of numbers and updates the “validated” variable when a number reaches 1. I’m also timing the entire process (to compare vs a single threaded computation)
The problem I’m having is that there is never any consistency when I print out the “validated” int at the end of the program, so I am guessing that either the threads are writing over each other, or the main thread is completing before the others, thus printing an incomplete number. I’m also assuming that the clock() calculations will be off too, if the main thread is completing before the others. So, how do I STOP the main thread from continuing until the other threads are completed (thus making it wait for a updated validated count and complete an accurate time measurement)? This is what I thought WaitForSingleObject did, but I’m guessing it just stops the main thread from EXITING, still allowing it to compute its other functions.
This is my first go at any multi-threading, and I don’t think I quite understand the workings of synchronization and the WaitForSingleObject command. Here is what I have so far in my main function:
EDIT: Here is my updated Main function and Collatz function. I modified it so that each thread is accessing a separate variable to avoid the synchronization issue, but the problem still persists. There is no consistent value when I print out “validated” *
EDIT AGAIN: Alright, so I removed the “thread” int per Mladen Janković, and just used a simple counter to distribute the different intervals to the created threads. There is now a consistent, correct value for “validated”. HOWEVER, I still cannot get the program to actually finish when there are 1,000,000 numbers. Testing it for 100,000 or even 10,000 works flawlessly, but when I up it to 1,000,000 numbers, the program runs indefinitely (hours) without actually returning a values. I’m guessing that it is getting stuck at a particular value (eg 750831 as Martin James pointed out). I tried substituting int for long int, but it seems that it still suffers from overflow. Any suggestions? And thanks for the immense help.
LAST EDIT: Alright, so I just used long long instead of int and now the program works flawlessly. Thanks for the help everyone!
void main()
{
clock_t start;
clock_t finish;
unsigned int thread = 0;
start = clock();
HANDLE h1 = (HANDLE)_beginthreadex(NULL, 0, collatz_thread, NULL, 0, NULL);
HANDLE h2 = (HANDLE)_beginthreadex(NULL, 0, collatz_thread, NULL, 0, NULL);
HANDLE h3 = (HANDLE)_beginthreadex(NULL, 0, collatz_thread, NULL, 0, NULL);
for (int i = 750001 ; i <= 1000000 ; i++) { collatz(i, 4); }
WaitForSingleObject( h1, INFINITE );
WaitForSingleObject( h2, INFINITE );
WaitForSingleObject( h3, INFINITE );
finish = clock() - start;
double time = finish / (double) CLOCKS_PER_SEC;
validated = v1 + v2 + v3 + v4;
cout << validated << " numbers validated." << endl;
cout << endl << time << " seconds." << endl;
}
unsigned _stdcall collatz_thread (void* n)
{
selection++; // selects a different interval each time collatz_thread is called
switch (selection) {
case 1:
for (int i = 1 ; i <= 250000; i++) { collatz(i, 1); }
break;
case 2:
for (int i = 250001 ; i <= 500000; i++) { collatz(i, 2); }
break;
case 3:
for (int i = 500001 ; i <= 750000; i++) { collatz(i, 3); }
break;
}
return 0;
}
int collatz (int n, int thread)
{
int original = n;
while (n != 1) {
if (n%2 == 0)
n = (n/2);
else
n = (3*n + 1);
}
if (n == 1) {
switch (thread) {
case 1:
v1++;
break;
case 2:
v2++;
break;
case 3:
v3++;
break;
case 4:
v4++;
break;
}
return n;
}
}
You need to synchronize access to
validatedif it is shared variable. The easies way is to useInterlockedIncrementfunction instead of standard++operator when you want to increment it. Another approach is to use some kind of synchronization object like spinlock or mutex when you access the shared variable, but that is overkill if you just need to synchronize increment operation.If you want more details, please provide code of
collatzfunction.As ‘usr’ suggested, for the better performance you can use separate variable fore each thread and then sum them in the main thread. In this scenario, you should pad those variables in such a way so they don’t share same cache-line to avoid false sharing.
You haven’t provided
collatz_threadfunction, which could be another cause of inconsistent results. The reason is you pass pointer to variable (&thread) that stores thread # which changes between the calls that create new threads, so depending on the state of the OS’s scheduler the new threads might not get a chance to start whilethreadvariable is already changed to have another value, so then you’ll have more then one thread doing the same set of data, while other sets might be skipped. Since the behavior depends on current state of thread scheduler it’s pretty much unpredictable.The solution is cast
threadvariable tovoid*instead of passing its address and then incollatz_threadfunction cast it back toint:HANDLE h1 = (HANDLE)_beginthreadex(NULL, 0, collatz_thread, (void*)thread, 0, NULL);And as Martin suggested, you possibly have integer overflow, but it shouldn’t cause inconsistent results, just wrong results, but consistent nevertheless.