Thread Management Sample Code
The following code snippet illustrates how to create a thread in suspended mode, specify a thread function and parameters, change the thread priority, resume the thread, and wait for the thread to finish its processing and exit. In the last step, the following code snippet demonstrates how to check the error code returned from the thread function.
// Structure used to pass parameters to the thread.
typedef struct {
BOOL bStop;
} THREAD_PARAM_T
// Thread function
DWORD WINAPI ThreadProc(LPVOID lpParameter) {
// Perform thread actions...
// Exit the thread.
return ERROR_SUCCESS;
}
BOOL bRet = FALSE;
THREAD_PARAM_T threadParams;
threadParams.bStop = FALSE;
DWORD dwExitCodeValue = 0;
// Create the thread in suspended mode.
HANDLE hThread = CreateThread(NULL, 0, ThreadProc,
(LPVOID) &threadParams, CREATE_SUSPENDED, NULL);
if (hThread == NULL) {
// Manage the error...
} else {
// Change the Thread priority.
CeSetThreadPriority(hThread, 200);
// Resume the thread, the new thread will run now.
ResumeThread(hThread);
// Perform parallel actions with the current thread...
// Wait until the new thread exits.
WaitForSingleObject(hThread, INFINITE);
// Get the thread exit code
// to identify the reason for the thread exiting
// and potentially detect errors
// if the return value is an error code value.
bRet = GetExitCodeThread(hThread, &dwExitCodeValue);
if (bRet && (ERROR_SUCCESS == dwExitCodeValue)) {
// Thread exited without errors.
} else {
// Thread exited with an error.
}
// Don't forget to close the thread handle
CloseHandle(hThread);
}
Thread Synchronization
The real art of multithreaded programming lies in avoiding deadlocks, protecting access to resources, and ensuring thread synchronization. Windows Embedded CE provides several kernel objects to synchronize resource access for threads in drivers or applications, such as critical sections, mutexes, semaphores, events, and interlocks functions. Yet, the choice of the object depends on the task that you want to accomplish.
Critical Sections
Critical sections are objects that synchronize threads and guard access to resources within a single process. A critical section cannot be shared between processes. To access a resource protected by a critical section, a thread calls the EnterCriticalSection function. This function blocks the thread until the critical section is available.
In some situations, blocking the thread execution might not be efficient. For example, if you want to use an optional resource that might never be available, calling the EnterCriticalSection function blocks your thread and consumes kernel resources without performing any processing on the optional resource. It is more efficient in this case to use a critical section without blocking by calling the TryEnterCriticalSection function. This function attempts to grab the critical section and returns immediately if the critical section cannot be used. The thread can then continue along an alternative code path, such as to prompt the user for input or to plug in a missing device.
Having obtained the critical section object through EnterCriticalSection or TryEnterCriticalSection, the thread enjoys exclusive access to the resource. No other thread can access this resource until the current thread calls the LeaveCriticalSection function to release the critical section object. Among other things, this mechanism highlights why you should not use the TerminateThread function to terminate threads. TerminateThread does not perform cleanup. If the terminated thread owned a critical section, the protected resource becomes unusable until the user restarts the application.
Table 3-11 lists the most important functions that you can use to work with critical section objects for thread synchronization purposes.
Table 3-11 Critical Section API
Function | Description |
---|---|
InitializeCriticalSection | Create and initialize a critical section object. |
DeleteCriticalSection | Destroy a critical section object. |
EnterCriticalSection | Grab a critical section object. |
TryEnterCriticalSection | Try to grab a critical section object. |
LeaveCriticalSection | Release a critical section object. |
Mutexes
Whereas critical sections are limited to a single process, mutexes can coordinate mutually exclusive access to resources shared between multiple processes. A mutex is a kernel object that facilitates inter-process synchronization. Call the CreateMutex function to create a mutex. The creating thread can specify a name for the mutex object at creation time, although it is possible to create an unnamed mutex. Threads in other processes can also call CreateMutex and specify the same name. However, these subsequent calls do not create new kernel objects, but instead return a handle to the existing mutex. At this point, the threads in the separate processes can use the mutex object to synchronize access to the protected shared resource.
The state of a mutex object is signaled when no thread owns it and non-signaled when one thread has ownership. A thread must use one of the wait functions, WaitForSingleObject or WaitForMultipleObjects, to request ownership. You can specify a timeout value to resume thread processing along an alternative code path if the mutex does not become available during the wait interval. On the other hand, if the mutex becomes available and ownership is granted to the current thread, do not forget to call ReleaseMutex for each time that the mutex satisfied a wait in order to release the mutex object for other threads. This is important because a thread can call a wait function multiple times, such as in a loop, without blocking its own execution. The system does not block the owning thread to avoid a deadlock situation, but the thread must still call ReleaseMutex as many times as the wait function to release the mutex.