Developing Fast Thread-Safe Code

Environment: VC 5/6, Windows 9X/NTX.X

Introduction

One of the many great features in Windows 9x and Windows NT is multiple threads. The power of multithreaded programming requires responsible programming with detailed design of the system. It's up to you to ensure that the CPU switches between threads seamlessly. For example, you might have data (such as a linked list) that can be accessed by more than one thread. Your code needs to ensure that a thread switch at the wrong moment won't leave your data in an inconsistent state. You prevent thread-switching problems by using synchronization objects.

In Win32, there are four types of synchronization objects. For synchronizing threads in the same or different processes, the Win32 API provides mutexes, events, and semaphores. The fourth type of synchronization object, critical sections, works only for threads within the same process. The problem I'm examining involves critical sections and an operating system behaviour.

Critical Section

When multiple threads have shared access to the same data, the threads can interfere with one another. A critical section object protects a section of code from being accessed by more than one thread. A critical section is limited, however, to only one process or DLL and cannot be shared with other processes. The advantage of critical sections is that they are less CPU intensive than the other synchronization methods. To use a critical section to guard a particular piece of code, you call EnterCriticalSection and pass in the address of a CRITICAL_SECTION structure. At the end of the code to be guarded, you call LeaveCriticalSection, passing in the same address of the CRITICAL_SECTION structure used earlier. Critical sections work by having a thread call the EnterCriticalSection or TryEnterCriticalSection functions to indicate that it has entered a critical section of code. If another thread calls EnterCriticalSection and references the same critical section object, it is blocked until the first thread calls the LeaveCriticalSection function.

A critical section can also protect more than one non-contiguous section of code as long as all such sections of code are protected by the same critical section object. For example, sections of code to add, delete, or modify a particular data can be protected by using the same critical section object so that only one of these sections of code will be able to access the data at a time.

You also need to initialize and destroy the critical section, but these two actions only need to occur once per instance of the program. Incidentally, the CRITICAL_SECTION structure that you pass to the critical section functions has to be either a global variable or in an allocated memory block. Never try to be smart like me and declare your CRITICAL_SECTION structure as a local variable within a function.

To Initialize the CRITIAL_SECTION, use the InitializeCriticalSection API. After initializing the CRITICAL_SECTION, a thread that successfully calls EnterCriticalSection is said to own the critical section. The thread continues to own the critical section until it calls LeaveCriticalSection. If a second thread comes along and calls code that's guarded by a critical section, the second thread will block inside the call to EnterCriticalSection. The only way for the second thread to continue executing is for the first thread to give up ownership of the critical section by calling LeaveCriticalSection. In this way, critical sections are able to ensure that only one thread at a time can execute through the guarded region of code. Delete it if it is no longer need using DeleteCriticalSection.

What Is Actually Happening

A critical section is a section of code that requires exclusive access to some set of shared data before it can be executed and that is used only by the threads within a single process. A critical section is like a turnstile through which only one thread at a time may pass, working as follows:

  1. To ensure that no more than one thread at a time accesses shared data, a process's primary thread allocates a global CRITICAL_SECTION data structure and initializes its members. A thread entering a critical section calls the Win32 function EnterCriticalSection and modifies the data structure's members.
  2. A thread attempting to enter a critical section calls EnterCriticalSection, which checks to see whether the CRITICAL_SECTION data structure has been modified. If so, another thread is currently in the critical section and the subsequent thread is put to sleep. A thread leaving a critical section calls LeaveCriticalSection, which resets the data structure. When a thread leaves a critical section, Microsoft Windows NT or Microsoft Windows 2000 wakes up one of the sleeping threads, which then enters the critical section.

Differences Between Other Synchronizing Objects

Critical sections are significantly faster than mutex kernel objects—or any other kernel object. Another important feature is that, by using Critical Sections, we can create functions that are as powerful as an Interlocked family of functions. The Interlocked functions are implemented entirely in user-mode space and do not require your thread to go from user mode to kernel mode and back again. For this reason, entering a critical section typically requires only 10 or so CPU instructions to execute. On the flip side, when you call WaitForSingleObject and the like, you are forcing your thread to transition to kernel mode and back. This transition typically requires 600 CPU instructions on an x86 processor. That's a huge difference!

You're right to want to use a critical section instead of a mutex to increase your performance. Critical sections are fast—as long as there is no contention for the shared resource. As soon as a thread attempts to enter a critical section owned by another thread, critical sections degrade to using an event kernel object requiring approximately 600 CPU instructions to enter. Because contention is so rare, entering a critical section usually takes the high-speed, 10 CPU instruction paths.

A critical section object performs exactly the same function as a mutex except that critical sections may not be shared. They are visible only within a single process. Critical sections and mutexes both allow only one thread to own them at a time, but critical sections work more quickly and involve less overhead.

The functions for working with critical sections do not use the same terminology as the functions for working with mutexes, but they do roughly the same things. Instead of creating a critical section, you initialize it. Instead of waiting for it, you enter it. Instead of releasing it, you leave it. Instead of closing its handle, you delete the object.

Programming Thread Safe Functions

Programming a thread-safe class never be a burden. Here, one example is given.

In one program, a 32bit unsigned int will be updated by various threads. But the constraint is that threads should not access the variable simultaneously.

Declare one global variable of type CRITICAL_SECTION with:

CRITICAL_SECTION _csIncrementLock;

unsigned int nCount;

Initialize CRITICAL_SECTION on startup by using the Win32 API void InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection):

            ::InitializeCriticalSection(&_csIncrementLock);

function, which will increment the variable.

void InterLockIncrement(unsigned int *pnVal)
{
    ::EnterCriticalSection(&_csIncrementLock);
                           ++(*pnVal);
    ::LeaveCriticalSection(&_csIncrementLock);
}

Now, you can call the function from anywhere in the program, without bothering about the multiple access on the variable.


// In the thread function.

InterLockIncrement(&nCount);

Before Process termination, delete the CRITICAL_SECTION using the Win32 API void DeleteCriticalSection (LPCRITICAL_SECTION lpCriticalSection):

//Delete before termination.

::DeleteCriticalSection(&_csIncrementLock);

Hidden Dragon

Now, let's turn our attention to deadlock, the dreaded black hole of multithreaded programming. A deadlock occurs when two or more threads each already own a synchronization object (such as a critical section), and need to acquire another synchronization object to continue executing. Deadlocks inspire much fear and loathing, as they're usually timing dependent, and are often notoriously difficult to reproduce consistently. They're usually the result of logic flaws in your programming; so traditional debugging tools aren't much help in tracking them down.

As part of learning how to use multiple threads correctly, we programmers are supposed to constantly be on the lookout for logic flaws in our code where a deadlock can occur. When reviewing your code, always try to imagine how a thread switch at the wrong moment could throw a monkey wrench into the works.



Comments

  • fofkxjsum

    Posted by chuscumsMup on 06/08/2013 04:58am

    Practice with the obligation PGA Specialized tutor. [url=http://www.coachfactorysite.com/]コーチ アウトレット[/url],There are two key aspects to consider finding some sort of the sport of golf tutor: 1) their own capacity to train and also transform your life sport; as well as 2) their variety of contacts using university mentors. Very first, you might be doing to enhance what you like. The the sport of golf master could be Sergio garcia, however he or she can not teach you the way toplay much better, he / she just isn't strength combined with comfort. Subsequent, professional training furthermore accomplishes your second target, getting noticed by means of college instructors. [url=http://www.coachfactorysite.com/]コーチ 財布[/url],The instructor you ultimately choose ought to be famous and revered by simply college or university mentors, and also have numerous contacts with large and more compact colleges. Meeting and get the game of golf benefits the number of mentors these people knowand exactly how properly they understand them. Consult ex- in addition to present students of the particular prospective positives of the training capability. Make seen to the correct persons your personal intention to experience the sport of golf inside higher education. The most important shed pounds enlighten of your goals are your the sport of golf coach along with high school graduation the game of golf trainer, if you have just one. [url=http://www.coachfactorysite.com/]コーチ バッグ 2013[/url],They are the 2 different people using likely scarves in order to and also romantic relationships having school instructors. Also, they are the individuals as their praise will have probably the most fat. Participate in within junior golfing tournaments. College or university the sport of golf, just like every other sport activity, requires stress of level of competition.[url=http://www.coachfactorysite.com/]コーチ バッグ メンズ[/url], Coaches are aware that should a possible guitar player has become needed for levels of competition and fared well, they've been confronted with match tension. An individual gain events, even though which certainly better just perform in addition to performyour greatest. Great dozens under uncertain the weather is among the elements thatget an individual found. [url=http://www.coachfactorysite.com/]コーチ バッグ[/url],Practically every place across the nation features a younger playing golf relationship as well as youngster golf expedition in which conducts tourneys. [url=http://www.coachfactorysite.com/]http://www.coachfactorysite.com/[/url] コーチ バッグ,Discover individuals tournament as well as perform. Higher education instructors will be viewing your current results and recalling labels.

    Reply
  • error

    Posted by lakhan on 09/26/2012 09:29pm

    code to d yaar ...

    Reply
  • it's more... but need more..

    Posted by Legacy on 12/05/2003 12:00am

    Originally posted by: renolov deca

    This is more for me... i am in server dev since 1996.. but i did not see the articale in any site... expecting more ...could you write an article for Threading and Memory management..

    Reply
  • Nice article - easy to understand

    Posted by Legacy on 10/07/2003 12:00am

    Originally posted by: surendiran

    hi,
    Its a very nice article, which is easy to understand for anyone.

    It had helped me to work on threads easily.
    thanks a lot.

    Reply
  • excellent ,welldone and fantastic work

    Posted by Legacy on 09/20/2003 12:00am

    Originally posted by: aneer

    81314358

    Reply
  • Nice

    Posted by Legacy on 09/02/2003 12:00am

    Originally posted by: Linto Joseph Mathew


    It was really was nice and understandable for a newbie too. this article is really helpful for client server programming.

    Linto Joseph Mathew.

    Reply
  • Excellent

    Posted by Legacy on 08/29/2003 12:00am

    Originally posted by: Vinu C.T.

    i read ur article. very nice .
    i have very doubt in this part. Can u help my doubts in Threds and networks

    Reply
  • Thread & exception safety

    Posted by Legacy on 08/27/2003 12:00am

    Originally posted by: Mark C

    Interesting article, thanks! I have two comments...

    First, I would always try to encapsulate the thread safety mechanism with the data. This may not be suitable for a simple int but you often need to protect more than that. Second, for exception safety, I would suggest wrapping the enter/leave critical section code in a simple object that is used as a local variable. Thus, however the function terminates the call to 'leave critical section' still occurs.

    Example: I needed to protect a queue of objects. I derived from a suitable std class and added functions for insert and remove. This new class initialises a CRITICAL_SECTION object druing construction and tidies up during destruction. An instance of a small helper class is then declared at the start of each function that needs protecting:

    class CCSectionGuard
    {
    public:
    CCSectionGuard( CRITICAL_SECTION& csection )
    : m_csection( csection )
    {
    EnterCriticalSection( &csection );
    }
    ~CCSectionGuard( void )
    {
    LeaveCriticalSection( &m_csection );
    }
    private:
    CRITICAL_SECTION& m_csection;
    }

    used thus:

    MyContainer::Insert( data_type data )
    {
    CCSectionGuard guard( my_critical_section_object );

    ...do whatever work is required...

    ...critical section is auto-released during function exit
    }

    Hope this helps!

    ~ Mark C

    Reply
  • Well Done

    Posted by Legacy on 08/25/2003 12:00am

    Originally posted by: JJ

    Just because something is available elsewhere on the internet doesn't mean it cannot be posted here. Many users such as myself enjoy "one stop shopping". I think the author did a great job explaining the topic and I hope the author writes more equally enjoyable articles.

    Reply
  • Nice Article

    Posted by Legacy on 08/25/2003 12:00am

    Originally posted by: sunyy

    This is a nice article, though introductory. It might have been very useful during my earlier Win32 programming.

    Thanks
    Sunyy

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Do you know where your data is? Consumer cloud-based file sharing services store your sensitive company data on servers outside of your control, outside of your policy and regulatory guidelines – maybe even outside your country – and not managed by you. The potential for data leakage, security breaches, and harm to your business is enormous. Download this white paper to learn about file sync and share alternatives that allow you to manage and protect your sensitive data while integrating and …

  • Available On-Demand Today's changing workforce dynamics, economic challenges, and technological advances are placing immense pressure on business leaders to turn their focus on people – their most valuable asset – in order to remain competitive. Research shows that a significant number of new employees quit within one year of taking a new job*. Whether it's through a merger and acquisition, or standard hiring process, like any first impression, early experiences shape their opinions of their new …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds