At current few applications still use the Single-Thread model. Every application benefits a lot from the multi-threads model: quick UI response, concurrent workflow, etc. But there is a concern that if you could not master the multi-thread well, it would be your nightmare.
In this article, I'll give some introductions to the common sychronous skills that we used in Win32.
1. CRITICAL_SECTIONCRITICAL_SECTION is used to protect your resources. Here the resource includes the memory blocks, files, data structures, etc.
One mind is that CRITICAL_SECTION is not the kernel object. So the cost of using it is less that the mutex kernel object. I think at most cases we should prefer to this choice if you don't want to syschrous between the multiple processes.
So here is the steps that you can use it.
//1. declare a critical_section object
CRITICAL_SECTION cs;
//2. initialize it before use.
InitializeCriticalSection(&cs);
//3. try to enter into the critical section
//if success, this function will return. Otherwise, the calling thread will be forced to sleep.
EnterCriticalSection(&cs);
//do some stuff
//4. leave the critical_section
LeaveCriticalSection(&cs);
//5. if not used any more, delete it.
DeleteCriticalSection(&cs);
One point is that the thread can enter into the critical_section several times after it already enter into it. The only thing that you should be aware of is that you should call the LeaveCriticalSection() same times as calling the EnterCriticalSection.
Here are some suggestions about how to use the critical_section.
- Do not lock one resource for long time. This may cause other threads waiting and may lead to UI hang.
- Do not call the Sleep() or Wait...() during the critical_section.
2. Mutex - Mutual Exclusion
Mutex is a kernel object and need the user-mode and kernel-mode switch if using it, which means a lot of cost are necessary. But there is a good poing is that you can use the Mutex between sereval processes, which the CRITICAL_SECTION could not provide this funcnality.
//1. Create a mutex using the Win32 API
// you can also call OpenMutex() to open an existing mutex.
HANDLE hMutex = CreateMutex(NULL, true, "MSCAR");
//2. Lock the mutex
//you can call WaitForMultipleObjects() if you want to lock several mutex
HRESULT hr = WaitForSingleObject(hMutex, INFINITE);
//3. got the lock and do something
if(hr == WAIT_OBJECT_0)
{
//
}
//4. release the mutex. this can lead to other threads getting the mutex
ReleaseMutex(hMutex)
//5. Close the handle. This should not be forgotten otherwise lead to handle leak.
CloseHandle(hMutex);
3. SemaphoresA lot of articles introduces several scenarios about how to use the semaphore, such like a buffer. So here I don't want to take too much. Just show the steps.
//create a semaphore, with the max_count set.
HANDLE hSemaphore = CreateSemaphore(NULL, 0, 5, "COUNT");
//the following is two threads running.
//thread 1
{
//add some resource
long lCount;
ReleaseSemaphore(hSemaphore, 1, &lCount);
}
//thread 2
{
//wait for resources. If resource is more than 1, this thread will be wake up.
WaitForSingleThread(hSemaphore, INFINITE);
//do some handling
}
You may notice from the demo code that the thread 1 doesn't get the ownership of the semaphore, but calls the ReleaseSemaphore(). This is the difference betwen the mutex and semaphore. For semaphore, threads doesn't need to get the ownership to call the ReleaseSemaphore().
4. Events
Event is the most flexiable to use. Its only purpose is to become the singled or unsignled. For mutex kernel objects, it will become singled after the thread calls the Wait...() function and shortly return back to unsigned status. But for evetns, these two status will be controled by application. Application can set its status perfectly as it hopes.
There are also two types of events: Auto and Munual. Auto event means that this event will become unsignled automatically after this event becomes signled, which always some thread wakes up from sleep. Munual event means that the application needs to call ResetEvent() to set the event in unsigled status. I'll list the API here.
//Create the event
HANDLE CreateEvent();
//Set the event in singled status
BOOL SetEvent();
//set the event in unsingled status
BOOL ResetEvent();