Why do my threads execute sequentially?

Aug 1, 2009 at 3:31pm
Hello guies:

I'm a bit new to programming and C++, I tend to code a multithreading program project, but before discussing the project's problems, I copied a code example here about multithreading from this website: (http://advancedcppwithexamples.blogspot.com/2009/03/multi-threading-with-thread.html). I also read in "Modern MultiThreading" book and found a similar usage of critical section. The results shown in the website after running the program demonstrate that threads work in parallel (it displays one result for each thread, then, another iteratin of one result for each thread). However, when I ran the code, the results were shown sequentially, means, thread one completes all the loop, then, switch to thread two and it does its job. This does not demonstrate multithreaded behaviour.

Anyone knows where the problem is?
by the way, the example is tested on visual studio 8 and I'm using Borland C++ builder 6, My OS is Windows Vista (Core Duo processor), but I do not think this is a problem.

here is the example code and when the problem solved, I will try the solution on the main project, if not succeeded, I will discuss the main project with you guys.

Thanks for your cooperation in advanced.

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

CRITICAL_SECTION cs; // global CRITICAL_SECTION
void WINAPI ThreadFunc();
void WINAPI ThreadFunc2();
HANDLE Thread[2];
DWORD ThrdID;
DWORD ThrdID1;


//void multiplier ();


int main(int argc, char* argv[])
{

      InitializeCriticalSection(&cs);

      //for (int i = 0; i <=1; i++)

      Thread[0] = CreateThread(NULL, 0,(LPTHREAD_START_ROUTINE)ThreadFunc, NULL, 0, &ThrdID);
      Thread[1] = CreateThread(NULL, 0,(LPTHREAD_START_ROUTINE)ThreadFunc2, NULL, 0, &ThrdID1);


      WaitForMultipleObjects(2, Thread, TRUE, INFINITE);

      //for (int ii = 0; ii <=1; ii++)
      CloseHandle (Thread[0]);
      CloseHandle (Thread[1]);


      DeleteCriticalSection(&cs);

      getchar();
      return 0;
}


void WINAPI ThreadFunc()
{
    for (int t=0; t<=10; t++)
    //int d = multiplier (a, b);
    {
		EnterCriticalSection(&cs);
		// access critical section
        cout << "This is function# 1, from loop # " << t << " thrid ID: " << ThrdID <<endl;
        LeaveCriticalSection(&cs);
        }

}

void WINAPI ThreadFunc2()
{
   for (int w=0; w<=10; w++)
   //int d = multiplier (a, b);
   {
		EnterCriticalSection(&cs);
		// access critical section
		cout << "This is function# 2, from loop # " << w << "thrid ID: " << ThrdID1 <<endl;
        LeaveCriticalSection(&cs);
   }


}
Aug 1, 2009 at 4:17pm
There are many reasons for this. Here are two:

1) It is up to the OS to decide which thread gets CPU time and when. If you have multiple cores, then it's likely that each thread is being put in separate cores, but still no guarantee that each thread is running at the same time (one thread's core might be giving CPU time to another process while the other thread is running)

2) Entering a critical section basically locks a mutex. If the mutex is already locked, the thread will sleep until it notices that the mutex is no longer locked. At which time it will lock the mutex and proceed with code execution. However, since another thread may not be sleeping, it may be unlocking and relocking the mutex multiple times before the sleeping thread ever wakes up. This is probably what's happening to you:

1
2
3
4
5
EnterCriticalSection(&cs);
Dostuff();
LeaveCriticalSection(&cs);
 // <---- "key time"
EnterCriticalSection(&cs);


In order for the threads to alternate printing messages like you are expecting, two things would have to happen:

1) the sleeping thread would have to wake up during the "key time"
2) the sleeping thread would have to be given a higher priority than the other thread so that it locks the mutex before the other thread does.

Both of these conditions are out of your control. And are actually quite unlikely in my experience. Any website that led you to believe these messages would alternate sounds kind of iffy to me.


Anyway -- threads do run in parallel. However when you mutex like this, they can't be running in parallel because the mutex prevents it (that's kind of the point of a mutex), so doing a test like this isn't very effective.
Aug 1, 2009 at 4:43pm
Hi Disch

Thanks for your quick response.
Well, when you said
If the mutex is already locked, the thread will sleep until it notices that the mutex is no longer locked
did you mean that one thread will be given a random time to sleep while the other is running? because I got two different understandings:

1- it will be given random time to sleep.
2- it will be given a sleeping time that might equal to the amount of time required by the
second thread to complete its job, so that, there won't be an interferene while doing the job (this seems to be weak as it doesn't implement multithreading concept).

Second thing:

you said:
Both of these conditions are out of your control. And are actually quite unlikely in my experience
Did you mean that they are out of my control ever.. or only in the code I posted here? because I read that there is a possibility for, 'mutex or critical section' I can't remember now, to be timed so that other thread can enter the critical section eventhough if the first thread didn't complete its critical section's execution. If I can control that, would you please show me a prototype code that demonstrate this issue?

Aug 1, 2009 at 5:47pm
did you mean that one thread will be given a random time to sleep while the other is running?


It depends on the implementation, really. Typically, though, you can expect it to sleep in relatively small intervals between polling the mutex. Or it might have an "alarm" which wakes up all threads (or a single thread of its choosing) that is currently sleeping waiting for the mutex. These kinds of details are generally out of your control though. However the threading implementation does it, that's what you're stuck with.

Did you mean that they are out of my control ever


Out of your control in every threading lib I've ever seen, used, or heard about.

There are simply too many factors for you to reliably have this kind of control. The OS is juggling not only threads in your program, but all sorts of other threads in other processes. When you consider that you only have two cores on your machine, but possibly have dozens or even hundreds of threads running at any given time -- all of which need occasional attention -- you simply can't make such demands of the OS.

There are ways to make your threads take turns, though. Like maybe have another variable to track which thread is supposed to execute a job next. It gets tricky, though, and it usually isn't worth it.

I can't remember now, to be timed so that other thread can enter the critical section eventhough if the first thread didn't complete its critical section's execution. If I can control that, would you please show me a prototype code that demonstrate this issue?


Some APIs have the ability to "time out" a mutex lock, so that instead of sleeping until the mutex can be locked (ie: potential deadlock if the mutex is never able to be locked) it will give up after a certain period of time (typically a few milliseconds).

HOWEVER: failure to lock a mutex is a big deal. And you should not proceed as normal. The whole point of putting things behind a mutex is that they cannot be accessed by multiple threads at the same time. If your mutex lock failed, you don't have this guarantee, and therefore you should abort whatever task you were attempting to do.

EDIT:

I can't really give you example code on this -- as I'm not at all familiar with WinAPI threads, and don't really feel like looking it up. Sorry =(

EDIT2:

Removed my really, really poor example. It wouldn't have worked at all like I originally thought it would.
Last edited on Aug 1, 2009 at 6:02pm
Aug 1, 2009 at 10:31pm
Hi Disch:

I know my reply is a bit late, I had to go out for a while.
I'm so thankful to you Disch. You showed me some great details that I can benefit from.
For now, it seems that my posted question is answered. I will try to implement what I got from you and if there is anything else I will be happy to post my questions in this great forum and have it being answered by great guies like you Disch.

Thanks very much.
Topic archived. No new replies allowed.