The CRITICAL_SECTION Object
A critical section, as described earlier, is a section of code that only one thread can execute at a time; more than one thread executing the critical section concurrently can result in unpredictable and incorrect results.Windows provides the CRITICAL_SECTION object as a simple mechanism for implementing and enforcing the critical section concept.CRITICAL_SECTION (CS) objects are initialized and deleted but do not have handles and are not shared by other processes. A variable should be declared to be of type CRITICAL_SECTION. Threads enter and leave a CS, and only one thread at a time can be in a specific CS. A thread can, however, enter and leave a specific CS at several places in the program.To initialize and delete a CRITICAL_SECTION variable and its resources, use InitializeCriticalSection and DeleteCriticalSection, respectively.
VOID InitializeCriticalSection (
LPCRITICAL_SECTION lpCriticalSection)
VOID DeleteCriticalSection (
LPCRITICAL_SECTION lpCriticalSection)
EnterCriticalSection blocks a thread if another thread is in the section. The waiting thread unblocks when another thread executes LeaveCriticalSection. We say that a thread owns the CS once it returns from EnterCriticalSection, and LeaveCriticalSection relinquishes ownership. Always be certain to relinquish a CS; failure to do so will cause other threads to wait forever, even if the owning thread terminates.We will often say that a CS is locked or unlocked, and entering a CS is the same as locking the CS.
VOID EnterCriticalSection (
LPCRITICAL_SECTION lpCriticalSection)
VOID LeaveCriticalSection (
LPCRITICAL_SECTION lpCriticalSection)
If a thread already owns the CS, it can enter again without blocking; that is, CRITICAL_SECTIONs are recursive. A count is maintained so that the thread must leave as many times as it enters in order to unlock the CS for other threads. This capability can be useful in implementing recursive functions and making shared library functions thread-safe.Leaving a CS that a thread does not own can produce unpredictable results, including thread blockage.There is no time-out from EnterCriticalSection; a thread will block forever if the owning thread never leaves the CS. You can, however, test or poll to see whether another thread owns a CS using TRyEnterCriticalSection.
BOOL TryEnterCriticalSection (
LPCRITICAL_SECTION lpCriticalSection)
A trUE return value from tryEnterCriticalSection indicates that the calling thread now owns the CS, and a FALSE return indicates that some other thread already owns the CS.CRITICAL_SECTIONs have the advantage of not being kernel objects and are maintained in user space. This usually, but not always, provides performance improvements. We will discuss the performance benefit once kernel synchronization objects have been introduced.
Adjusting the Spin Count
Normally, if a thread finds that a CS is already owned when executing EnterCriticalSection, it enters the kernel and blocks until the CRITICAL_SECTION is released, which is time consuming. On SMP systems, however, you can require that the thread try again before blocking as the owning thread may be running on a separate processor and could release the CS at any time. This can be useful for performance when there is high contention among threads for a single CRITICAL_SECTION. Performance implications are discussed later in this chapter and the next.The two functions to adjust spin count are SetCriticalSectionSpinCount, which allows you to adjust the count dynamically, and InitializeCriticalSectionAndSpinCount, which is a substitute for InitializeCriticalSection. Spin count tuning is a topic in Chapter 9.
