Beginning win32 multithreading

In an attempt to understand windows multithreading a little more thoroughly, I’ve decided to write about the simple concepts associated with multithreading. This is mainly to clarify it in my own head but maybe it will help someone else out too. If any of this information is wrong, don’t yell at me.:-)Please just post a comment and I’ll look into it and/or fix it. So here we go.

Thread: I’m not going to define a thread. Just know that a thread is a kernel object, which means that when a thread finishes executing, it gets signaled. If you use “WaitForSingleObject();” for example to wait for it to finish executing, it is safer and doesn’t eat up CPU time like a spin lock would.

Signaled: Kernel objects get signaled (threads, mutexes, etc.). When a kernel object gets signaled, “WaitForSingleObject()” wakes up. The same goes for other “Wait*()” functions. In the case of threads: running = nonsignaled, when a thread terminates = signaled.

Atomic Operation: A single operation that the processor can not perform a context switch in between.

Context Switch: When the processor halts execution of a thread, and then continues executing another thread from where it left off previously. The cause of race conditions when multithreading, but a necessary feature on processors.

Spin Lock: Eats CPU time, not atomic. Basically the predecessor to critical sections. Basically a “for” loop that waits around until a thread returns. This is the worst way to do multithreading, don’t use spin locks at all.

Race Condition: The easiest way to conceptualize a race condition is to think of it this way: A thread writes data and soon after, another thread blindly overwrites the data that was just written by the first thread. Critical Sections resolve this.

Critical Section: If you only have one resource that needs sharing between multiple threads, you use a “Critical Section.” Critical sections are not kernel objects and prevent race conditions between multiple threads. The reason you use critical sections is to make certain areas of your code / data atomic so that a context switch doesn’t fowl up your data. Note: You can specify different areas of your code as the same critical section.

Mutex: If you have multiple resources that need to be shared among threads, you do NOT use a critical section. This can result in a dead lock where one thread waits for the other thread to release a resource, but that thread is waiting on the first thread to release a resource it needs, so both end up waiting forever. So instead you use a mutex. A mutex is a kernel object and can be shared among threads as well as processes. Basically, a mutex says: “Execute this code if both (or however many the mutex was defined with) resources are available.” If that requirement isn’t satisfied, then the code isn’t run. For example: If only one resource was available but the mutex was defined for two resources, then the code would not run.

Note: Mutexes introduce race conditions now, the very thing that critical sections fixed. Also, it’s worth noting that you can still get a dead lock with mutexes if you use “WaitForSingleObject().” Instead, you must use “WaitForMultipleObjects()” to avoid dead locks.

Win32 functions associated with multithreading
General Win32 Thread Functions: You can look up the parameters yourself on msdn.
CreateThread();
GetExitCodeThread();
ExitThread();
WaitForSingleObject();
WaitForMultipleObjects();
MsgWaitForMultipleObjects();

Critical Section: Sharing one resource
InitializeCriticalSection();
DeleteCriticalSection();
EnterCriticalSection();
LeaveCriticalSection();

Mutex: Sharing multiple resources
CreateMutex();
OpenMutex();
WaitForSingleObject();
WaitForMultipleObjects();
MsgWaitForMultipleObjects();
ReleaseMutex();
CloseHandle();

Leave a Reply

You must be logged in to post a comment.