Click here to Skip to main content
Click here to Skip to main content

Introduction to Multi-threaded Code

, 26 Mar 2000 CPOL
Rate this:
Please Sign up or sign in to vote.
Synchronizing worker threads using 3 different methods
  • Download no synchronisation example - 31 Kb
  • Download critical section example - 9 Kb
  • Download mutex example - 9 Kb
  • Download event object example - 9 Kb
  • Someone recently asked me what I recommend for synchronizing worker threads and I suggested setting an event. This person's response was that you could not do that since worker threads do not support a message pump (UI threads are required to support messages). The confusion here is that events and messages are different animals under windows.

    I have forgotten where I originally copied these examples, but I found them to be interesting because of their simplicity. If anyone is aware of the author of this code, I would appreciate hearing from you, so I can give him/her credit.

    Note there is considerable support for threads in MFC that is not covered here. API's like _beginthread (a C runtime library call) would likely be replaced by MFC API's like AfxBeginThread in an MFC application.

    No Synchronization

    This first example illustrates two unsynchronized threads. The main loop, which is the primary thread of a process, prints the contents of a global array of integers. The thread called "Thread" continuously populates the global array of integers.

      #include <process.h>
      #include <stdio.h>
      
      int a[ 5 ];
      
      void Thread( void* pParams )
      { int i, num = 0;
      
        while ( 1 )
        { 
           for ( i = 0; i < 5; i++ ) a[ i ] = num;
           num++;
        }
      }
      
      int main( void )
      { 
         _beginthread( Thread, 0, NULL );
      
         while( 1 )
            printf("%d %d %d %d %d\n", 
                   a[ 0 ], a[ 1 ], a[ 2 ],
                   a[ 3 ], a[ 4 ] );
      
       return 0;
      }
    

    Note in this sample output, the numbers in red illustrate a state where the primary thread preempted the secondary thread in the middle of populating the values of the array:

    81751652 81751652 81751651 81751651 81751651
    81751652 81751652 81751651 81751651 81751651
    83348630 83348630 83348630 83348629 83348629
    83348630 83348630 83348630 83348629 83348629
    83348630 83348630 83348630 83348629 83348629

    If you are running Windows 9x/NT/2000, you can run this program by clicking here. After the program begins to run, press the "Pause" key stop the display output (this stops the primary thread's I/O, but the secondary thread continues to run in the background) and any other key to restart it.

    Critical Section Objects

    What if your main thread needed all elements of the array to processed prior to reading? One solution is to use a critical section.

    Critical section objects provide synchronization similar to that provided by mutex objects, except critical section objects can be used only by the threads of a single process. Event, mutex, and semaphore objects can also be used in a single-process application, but critical section objects provide a slightly faster, more efficient mechanism for mutual-exclusion synchronization. Like a mutex object, a critical section object can be owned by only one thread at a time, which makes it useful for protecting a shared resource from simultaneous access. There is no guarantee about the order in which threads will obtain ownership of the critical section, however, the system will be fair to all threads.

      #include <windows.h>
      #include <process.h>
      #include <stdio.h>
      
      CRITICAL_SECTION cs;
      int a[ 5 ];
      
      void Thread( void* pParams )
      {
        int i, num = 0;
      
        while ( TRUE )
        {
           EnterCriticalSection( &cs );
           for ( i = 0; i < 5; i++ ) a[ i ] = num;
           LeaveCriticalSection( &cs );
           num++;
        }
      }
      
      int main( void )
    
      { 
        InitializeCriticalSection( &cs );
        _beginthread( Thread, 0, NULL );
      
        while( TRUE )
        {
           EnterCriticalSection( &cs );
           printf( "%d %d %d %d %d\n", 
                   a[ 0 ], a[ 1 ], a[ 2 ],
                   a[ 3 ], a[ 4 ] );
           LeaveCriticalSection( &cs );
        }
        return 0;
      }
    

    If you are running Windows 9x/NT/2000, you can run this program by clicking here.

    Mutex Objects

    A mutex object is a synchronization object whose state is set to signaled when it is not owned by any thread, and non-signaled when it is owned. Only one thread at a time can own a mutex object, whose name comes from the fact that it is useful in coordinating mutually exclusive access to a shared resource. For example, to prevent two threads from writing to shared memory at the same time, each thread waits for ownership of a mutex object before executing the code that accesses the memory. After writing to the shared memory, the thread releases the mutex object.

    Two or more processes can call CreateMutex to create the same named mutex. The first process actually creates the mutex, and subsequent processes open a handle to the existing mutex. This enables multiple processes to get handles of the same mutex, while relieving the user of the responsibility of ensuring that the creating process is started first. When using this technique, you should set the bInitialOwner flag to FALSE; otherwise, it can be difficult to be certain which process has initial ownership.

    Multiple processes can have handles of the same mutex object, enabling use of the object for interprocess synchronization. The following object-sharing mechanisms are available:

    • A child process created by the CreateProcess function can inherit a handle to a mutex object if the lpMutexAttributes parameter of CreateMutex enabled inheritance.
    • A process can specify the mutex-object handle in a call to the DuplicateHandle function to create a duplicate handle that can be used by another process.
    • A process can specify the name of a mutex object in a call to the OpenMutex or CreateMutex function.

    Generally speaking, if you are synchronizing threads within the same process, a critical section object is more efficient.

      #include <windows.h>
      #include <process.h>
      #include <stdio.h>
      
      HANDLE hMutex;
      int a[ 5 ];
      
      void Thread( void* pParams )
      { 
         int i, num = 0;
      
         while ( TRUE )
         { 
            WaitForSingleObject( hMutex, INFINITE );
            for ( i = 0; i < 5; i++ ) a[ i ] = num;
            ReleaseMutex( hMutex );
            num++;
         }
      }
      
      int main( void )
      {
         hMutex = CreateMutex( NULL, FALSE, NULL );
         _beginthread( Thread, 0, NULL );
      
         while( TRUE )
    
         {
            WaitForSingleObject( hMutex, INFINITE );
            printf( "%d %d %d %d %d\n", 
                    a[ 0 ], a[ 1 ], a[ 2 ],
                    a[ 3 ], a[ 4 ] );
            ReleaseMutex( hMutex );
         }
         return 0;
      }

    If you are running Windows 9x/NT/2000, you can run this program by clicking here.

    Event Objects

    What if we want to force the secondary thread to run each time the primary thread finishes printing the contents of the global array, so that the values in each line of output is only incremented by one?

    An event object is a synchronization object whose state can be explicitly set to signaled by use of the SetEvent or PulseEvent function. Following are the two types of event object.

    Object Description
    Manual-reset event An event object whose state remains signaled until it is explicitly reset to non-signaled by the ResetEvent function. While it is signaled, any number of waiting threads, or threads that subsequently specify the same event object in one of the wait functions, can be released.
    Auto-reset event An event object whose state remains signaled until a single waiting thread is released, at which time the system automatically sets the state to non-signaled. If no threads are waiting, the event object's state remains signaled.

    The event object is useful in sending a signal to a thread indicating that a particular event has occurred. For example, in overlapped input and output, the system sets a specified event object to the signaled state when the overlapped operation has been completed. A single thread can specify different event objects in several simultaneous overlapped operations, then use one of the multiple-object wait functions to wait for the state of any one of the event objects to be signaled.

    A thread uses the CreateEvent function to create an event object. The creating thread specifies the initial state of the object and whether it is a manual-reset or auto-reset event object. The creating thread can also specify a name for the event object. Threads in other processes can open a handle to an existing event object by specifying its name in a call to the OpenEvent function. For additional information about names for mutex, event, semaphore, and timer objects, see Interprocess Synchronization.

    A thread can use the PulseEvent function to set the state of an event object to signaled and then reset it to non-signaled after releasing the appropriate number of waiting threads. For a manual-reset event object, all waiting threads are released. For an auto-reset event object, the function releases only a single waiting thread, even if multiple threads are waiting. If no threads are waiting, PulseEvent simply sets the state of the event object to non-signaled and returns.

      #include <windows.h>
      #include <process.h>
      #include <stdio.h>
      
      HANDLE hEvent1, hEvent2;
      int a[ 5 ];
      
      void Thread( void* pParams )
      {
         int i, num = 0;
    
         while ( TRUE )
         {
            WaitForSingleObject( hEvent2, INFINITE );
            for ( i = 0; i < 5; i++ ) a[ i ] = num;
            SetEvent( hEvent1 );
            num++;
         }
      }
      
      int main( void )
      {
         hEvent1 = CreateEvent( NULL, FALSE, TRUE, NULL );
         hEvent2 = CreateEvent( NULL, FALSE, FALSE, NULL );
      
         _beginthread( Thread, 0, NULL );
      
         while( TRUE )
         { 
            WaitForSingleObject( hEvent1, INFINITE );
            printf( "%d %d %d %d %d\n", 
                    a[ 0 ], a[ 1 ], a[ 2 ],
                    a[ 3 ], a[ 4 ] );
            SetEvent( hEvent2 );
         }
         return 0;
      }
    

    If you are running Windows 9x/NT/2000, you can run this program by clicking here.

    Summary of Synchronization Objects

    The MSDN News for July/August 1998 has a front page article on Synchronization Objects. The following table is from that article:

    Name Relative speed Cross process Resource counting Supported platforms
    Critical Section Fast No No (exclusive access) 9x/NT/CE
    Mutex Slow Yes No (exclusive access) 9x/NT/CE
    Semaphore Slow Yes Automatic 9x/NT
    Event Slow Yes Yes 9x/NT/CE
    Metered Section Fast Yes Automatic 9x/NT/CE

    License

    This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

    Share

    About the Author

    William T. Block
    Software Developer (Senior) Baker Hughes
    United States United States
    Bill's recent projects include graphical displays and printing of real-time data for the Oil Industry.
     
    "I started programming Windows' applications right after the release of Windows 1.0 and I am now actively working with Microsoft .NET"
     
    He currently works for Baker Hughes in the Houston, Texas area.

    Comments and Discussions

     
    GeneralMy vote of 2 PinmemberMatra Divat21-Mar-13 5:04 
    QuestionCritical Section Objects example: editing proposal PinmemberStefano Capelli8-Nov-12 7:04 
    QuestionGood Article Pinmembersrikanth_shettigar22-Mar-12 18:25 
    Questionmy 5 Pinmembermarc ochsenmeier22-Jan-12 7:01 
    GeneralMy vote of 5 PinmemberPranit Kothari26-Dec-11 16:56 
    Generalthanks Pinmembercute_friend707725-Feb-10 1:52 
    Generalthanks Pinmembercute_friend707725-Feb-10 1:52 
    QuestionDoes it work in Linux system? Pinmembersppradip25-Jan-10 22:25 
    QuestionEvent Object PinmemberMahmoud Fayez31-Oct-07 3:33 
    GeneralExecuting Two Functions Simultaneously PinmemberMs. Agrawal1-Apr-07 21:55 
    GeneralSynchronization PinmemberAbhitha26-Mar-07 0:18 
    QuestionProblem Thread synchronization using Events PinmemberMuralish15-Sep-06 20:40 
    QuestionA little question about the source code above [modified] PinmemberHDZ@CN27-Jun-06 17:16 
    GeneralGlobal Mutex Problem PinmemberDavidLoeb8-Jun-06 20:34 
    GeneralThanx - Suggestion PinmemberOsama E. Adly29-Mar-06 4:16 
    GeneralHandles leak PinmemberAnthony_Yio19-Nov-04 17:15 
    Generalvc++ PinsussAnonymous19-Oct-03 1:35 
    GeneralRe: vc++ PinmemberR.selvam6-Feb-04 0:18 
    GeneralRe: vc++ PinmemberLarry Dobson17-Jun-04 4:29 
    GeneralThanks for Event Objects PinmemberMoak16-Mar-03 12:14 
    GeneralRe: Thanks for Event Objects PinmemberWilliam T. Block16-Mar-03 19:18 
    QuestionCan not compile PinmemberEmiliano5-Mar-03 14:47 
    AnswerRe: Can not compile PinmemberPeter Hancock5-Mar-03 14:58 
    Generalwhy this code doesn't work PinsussAnonymous7-Dec-02 10:46 
    GeneralRe: why this code doesn't work PinmemberR.selvam19-Feb-04 23:53 
    Generalmulti-threading in java!!! Pinmemberspace_fire15-Oct-02 11:09 
    QuestionDoes this work in WinXP? PinmemberKranar4-Sep-02 21:03 
    AnswerRe: Does this work in WinXP? PinmemberWilliam T. Block5-Sep-02 4:29 
    GeneralRe: Does this work in WinXP? PinmemberKranar5-Sep-02 10:10 
    GeneralRe: Does this work in WinXP? PinmemberWilliam T. Block5-Sep-02 16:28 
    GeneralRe: Does this work in WinXP? PinmemberKranar5-Sep-02 17:42 
    GeneralMultiple Critical Sections PinmemberFlexMaster3-Jun-02 3:29 
    GeneralRe: Multiple Critical Sections PinmemberAbhitha26-Mar-07 0:23 
    QuestionWhat is the problem with this code? PinmemberEagerToLearn19-Dec-01 9:17 
    AnswerRe: What is the problem with this code? PinmemberTim Smith19-Dec-01 9:21 
    GeneralRe: What is the problem with this code? PinmemberEagerToLearn20-Dec-01 8:29 
    AnswerRe: What is the problem with this code? Pinmembermla15424-May-07 10:25 
    AnswerRe: What is the problem with this code? Pinmemberkalessil6-Sep-07 23:44 
    Questionhow about involved in mfc PinmemberJohnthan Chau25-Nov-01 6:51 
    GeneralShared Memeory within threads PinmemberAnonymous13-Nov-01 22:11 
    GeneralRe: Shared Memeory within threads PinmemberWilliam T. Block25-Nov-01 9:49 
    Generalusing _beginthread, pointing to a class function PinmemberAnonymous5-Nov-01 1:50 
    GeneralRe: using _beginthread, pointing to a class function PinmemberNikolay Mitev13-Mar-02 3:43 
    GeneralRe: using _beginthread, pointing to a class function PinmemberMoMad17-Aug-02 1:24 
    General_beginthread() or CreateThread() PinmemberDemir Ateser14-Dec-00 8:23 
    GeneralRe: _beginthread() or CreateThread() Pinmembergmes8-Apr-01 13:07 
    GeneralRe: _beginthread() or CreateThread() PinmemberAnonymous18-May-01 3:18 
    GeneralRe: _beginthread() or CreateThread() PinmemberMarco Bartalucci4-Jul-01 6:50 
    GeneralRe: _beginthread() or CreateThread() PinmemberAllan Hsieh4-Jul-01 7:07 
    Generalkill, unhandled exceptions ... PinsussVanhemmens Francis30-Aug-00 2:26 

    General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

    Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

    | Advertise | Privacy | Terms of Use | Mobile
    Web04 | 2.8.1411023.1 | Last Updated 27 Mar 2000
    Article Copyright 2000 by William T. Block
    Everything else Copyright © CodeProject, 1999-2014
    Layout: fixed | fluid