![]() ![]() A critical section identifies code that must be executed to completion before another piece of code can be executed. In the example presented in the previous section, the statements "g_fValue = g_fValue * g_fValue;" and "g_fValue = 3.0 + g_fValue;" should be marked as critical sections to ensure that both statements can be executed to completion before the other starts executing. If this is done, the only two possible results in g_fValue are 103 and 169. The spurious value of 130 will never occur. To create and use a critical section you should:
All the critical section functions take a single argument that is a pointer to the CRITICAL_SECTION structure. You should treat this structure as a black box and not use the members contained in it. The code in Listing 6.1 declares a critical section structure g_cs, and creates two threads using thread functions f1 and f2. Each thread function performs an operation on a global float value "g_fValue". Because each thread is accessing a global function, the critical section structure g_cs is used to synchronize access to the global variable. Listing 6.1 Using critical sectionsfloat g_fValue = 10.0; CRITICAL_SECTION g_cs; DWORD WINAPI f1(LPVOID); DWORD WINAPI f2(LPVOID); void Listing6_1() { HANDLE hThread1, hThread2; DWORD dwThreadID; g_fValue = 10.0; InitializeCriticalSection(&g_cs); hThread1 = CreateThread(NULL, 0, f1, NULL, 0, &dwThreadID); hThread2 = CreateThread(NULL, 0, f2, NULL, 0, &dwThreadID); // Wait until thread 1 and thread 2 completes WaitForSingleObject(hThread1, INFINITE); WaitForSingleObject(hThread2, INFINITE); DeleteCriticalSection(&g_cs); CloseHandle(hThread1); CloseHandle(hThread2); cout _T("Finished:") g_fValue endl; } DWORD WINAPI f1(LPVOID) { EnterCriticalSection(&g_cs); g_fValue = g_fValue * g_fValue; LeaveCriticalSection(&g_cs); return 0; } DWORD WINAPI f2(LPVOID) { EnterCriticalSection(&g_cs); g_fValue = (float)3.0 + g_fValue; LeaveCriticalSection(&g_cs); return 0; } In Listing 6.1 you will notice that WaitForSingleObject is called twice, once for each of the two threads. This causes the function Listing6_1 to block until both threads have terminated. This is important, since the call to DeleteCriticalSection cannot be made until both threads have finished using the critical section. WaitForMultipleObjects cannot be used for this purpose, since in Windows CE WaitForMultipleObjects only blocks until one of the threads terminates. This is described in more detail later. Once one thread calls EnterCriticalSection, any other thread calling EnterCriticalSection using the same CRITICAL_SECTION structure will block until the first thread calls LeaveCriticalSection. When this happens, a thread blocked in EnterCriticalSection will unblock and can then execute the code in its critical section. Multiple threads can be blocked on calls to EnterCriticalSection, and you cannot predict which of these blocked threads will unblock. Note that creating a critical section does not ensure that the code in the critical section will execute to completion without interruption the normal thread-scheduling rules apply. The following rules should be applied when using critical sections:
You can declare multiple CRITICAL_SECTION structures to protect, for example, the access to different global variables. While this can improve the multithreading processing (since threads will not unnecessarily be blocked), it introduces the potential of deadlocks. For example, a thread could enter critical section 1 and then attempt to enter critical section 2. Another thread could enter critical section 2 and then attempt to enter critical section 1. If this happens simultaneously, a deadlock can occur. Using the rule described earlier, you can avoid this by always entering critical sections in the same order.
|