|< Windows Processes & Threads: Synchronization 3 | Main | Windows Processes & Threads: Synchronization 5 >| Site Index | Download |


 

 

 

MODULE AA

PROCESSES AND THREADS: SYNCHRONIZATION

Part 4: Program Examples

 

 

 

What are in this Module?

  1. Using Named Objects

  2. Mutex (Mutually Exclusive)

  3. Semaphore (Using Semaphore for Synchronization)

  4. Waiting for Multiple Objects

  5. Another example: Obtaining Directory Change Notifications

  6. Waiting in a Message Loop

  7. Using Mutex Objects

  8. Using Semaphore Objects

  9. Program Examples

  • Using named objects – Mutex & Semaphore.

  • Waiting for multiple objects.

  • Waiting in a message loop.

  • Using mutex objects.

  • Using semaphore objects.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

My Training Period: yy hours. Before you begin, read some instruction here. Functions and structure used in the program examples were dumped at Win32 functions & structures.

 

Win32 abilities that supposed to be acquired:

Note: For Multithreaded program examples, you have to set your project to Multithread project type.

 

Using Named Objects

 

The following examples illustrate the use of object names by creating and opening named objects.

 

Mutex (Mutually Exclusive)

 

The first process uses the CreateMutex() function to create the mutex object. Note that the function succeeds even if there is an existing object with the same name.

// for WinXp

#define _WIN32_WINNT 0x0501

#include <windows.h>

#include <stdio.h>

 

int main()

{

// one process creates the mutex object.

HANDLE hMutex;

char * MName = "MyMutex";

 

hMutex = CreateMutex(

    NULL,        // no security descriptor

    FALSE,     // mutex not owned

    MName);   // object name

 

if (hMutex == NULL)

    printf("CreateMutex(): error: %d.\n", GetLastError());

else

{

    if (GetLastError() == ERROR_ALREADY_EXISTS)

       printf("CreateMutex(): opened existing %s mutex.\n", MName);

    else

       printf("CreateMutex(): new %s mutex successfully created.\n", MName);

}

 

 return 0;

}

 

A sample output:

Process & Thread synchronization C program example: Mutex

 

The second process uses the OpenMutex() function to open a handle to the existing mutex. This function fails if a mutex object with the specified name does not exist. The access parameter requests full access to the mutex object, which is necessary for the handle to be used in any of the wait functions.

// for WinXp

#define _WIN32_WINNT 0x0501

#include <windows.h>

#include <stdio.h>

 

int main()

{

HANDLE hMutex1;

 

hMutex1 = OpenMutex(

    MUTEX_ALL_ACCESS, // request full access

    FALSE,                              // handle not inheritable

    MName);                            // object name

 

if (hMutex1 == NULL)

printf("OpenMutex(): error: %d.\n", GetLastError());

else

       printf("OpenMutex(): %s mutex opened successfully.\n", MName);

 

return 0;

}

 

A sample output:

 

Process & Thread synchronization C program example: OpenMutex()

// for WinXp

#define _WIN32_WINNT 0x0501

#include <windows.h>

#include <stdio.h>

 

int main()

{

// one process creates the mutex object.

HANDLE hMutex;

char * MName = "MyMutex";

 

hMutex = CreateMutex(

    NULL,        // no security descriptor

    FALSE,     // mutex not owned

    MName);   // object name

 

if (hMutex == NULL)

    printf("CreateMutex(): error: %d.\n", GetLastError());

else

{

    if (GetLastError() == ERROR_ALREADY_EXISTS)

       printf("CreateMutex(): opened existing %s mutex.\n", MName);

    else

       printf("CreateMutex(): new %s mutex successfully created.\n", MName);

}

 

//=================================================================

HANDLE hMutex1;

 

hMutex1 = OpenMutex(

    MUTEX_ALL_ACCESS, // request full access

    FALSE,                              // handle not inheritable

    MName);                            // object name

 

if (hMutex1 == NULL)

       printf("OpenMutex(): error: %d.\n", GetLastError());

else

       printf("OpenMutex(): %s mutex opened successfully.\n", MName);

 

 return 0;

}

 

A sample output:

Process & Thread synchronization C program example: CreateMutex()

 

Semaphore (Using Semaphore for Synchronization)

 

The following example uses the CreateSemaphore() function to illustrate a named-object creation operation that fails if the object already exists.

// for WinXp

#define _WIN32_WINNT 0x0501

#include <windows.h>

#include <stdio.h>

 

HANDLE CreateNewSemaphore(LPSECURITY_ATTRIBUTES lpsa, LONG cInitial, LONG cMax, LPTSTR lpszName)

{

    HANDLE hSem;

    // create or open a named semaphore.

    hSem = CreateSemaphore(

        lpsa,               // security attributes, NULL = handle cannot be inherited

        cInitial,            // initial count

        cMax,             // maximum count

        lpszName);    // semaphore name

 

    // close handle, and return NULL if existing semaphore opened.

    if (hSem == NULL && GetLastError() == ERROR_ALREADY_EXISTS)

    {

        CloseHandle(hSem);

        return NULL;

    }

       else

       {

              printf("Checking the last error: %d.\n", GetLastError());

              printf("CreateNewSemaphore(): New semaphore was created successfully.\n");

       }

    // if new semaphore was created, return the handle.

    return hSem;

}

 

int main()

{

       LPSECURITY_ATTRIBUTES lpsa = NULL;

       LONG cInitial = 0;

       LONG cMax = 10;

       LPTSTR lpszName = "MySemaphore";

 

       HANDLE hSemaphore = CreateNewSemaphore(lpsa, cInitial, cMax, lpszName);

       return 0;

}

 

A sample output:

 

Process & Thread synchronization C program example: Semaphore

 

Waiting for Multiple Objects

 

The following example uses the CreateEvent() function to create two event objects. It then uses the WaitForMultipleObjects() function to wait for the state of one of the objects to be set to signaled.

// for WinXp

#define _WIN32_WINNT 0x0501

#include <windows.h>

#include <stdio.h>

 

int main()

{

HANDLE hEvents[2];

DWORD i, dwEvent;

 

// create two event objects.

for (i = 0; i < 2; i++)

{

    hEvents[i] = CreateEvent(

        NULL,     // no security attributes

        FALSE,  // auto-reset event object

        FALSE,  // initial state is non-signaled (suspended)

        NULL);    // unnamed object

 

    if (hEvents[i] == NULL)

    {

        printf("CreateEvent() error: %d\n", GetLastError());

        ExitProcess(0);

    }

    else

        printf("CreateEvent() for event #%d is OK.\n", i);

}

 

// the creating thread waits for other threads or processes to signal the event objects.

dwEvent = WaitForMultipleObjects(

    2,              // number of objects in array

    hEvents,  // array of objects

    FALSE,   // wait for any object instead of all...

    4000);      // 4000 ms wait....

 

printf("Some verification, the return value is: %d.\n", dwEvent);

printf("Another verification, the last error is: %d.\n", GetLastError());

 

// return value indicates which event is signaled.

switch (dwEvent)

{

    // hEvent[0] was signaled.

    case WAIT_OBJECT_0 + 0: // 0 + 0 = 0

printf("Event #%d is signaled.\n", dwEvent);

       // TODO: Perform tasks required by this event...

       break;

    // hEvent[1] was signaled.

    case WAIT_OBJECT_0 + 1: // 0 + 1 = 1

printf("Event #%d is signaled.\n", dwEvent);

       // TODO: Perform tasks required by this event...

       break;

 

    // return value is invalid.

    default:

printf("Wait error: %d\n", GetLastError());

// exit peacefully or use customize error message...

ExitProcess(0);

}

     return 0;

}

 

A sample output:

Process & Thread synchronization C program example: Event, CreateEvent()

 

Well our program run successfully but from the output there is error!  The WaitForMultipleObjects() returned unexpected value 258…I don’t know why :o), the GetLastError() returns is OK.  If you change the CreateEvent()’s third parameter to TRUE, the program is OK as shown in the following figure but our intention not right anymore because the event already in signaled state before calling the WaitForMultipleObjects().

 

Process & Thread synchronization C program example: Event, CreateEvent()

 

Another example: Obtaining Directory Change Notifications

 

An application can monitor the contents of a directory and its subdirectories by using change notifications. Waiting for a change notification is similar to having a read operation pending against a directory and, if necessary, its subdirectories. When something changes within the directory being watched, the read operation is completed. For example, an application can use these functions to update a directory listing whenever a file name within the monitored directory changes.

An application can specify a set of conditions that trigger a change notification by using the FindFirstChangeNotification() function. The conditions include changes to file names, directory names, attributes, file size, time of last write, and security. This function also returns a handle that can be waited on by using the wait functions. If the wait condition is satisfied, FindNextChangeNotification() can be used to provide a notification handle to wait on subsequent changes. The FindCloseChangeNotification() function closes the notification handle. However, these functions do not indicate the actual change that satisfied the wait condition. To retrieve information about the specific change as part of the notification, use the ReadDirectoryChangesW() function. This function also enables you to provide a completion routine.

The following example monitors the directory tree starting at F:\ for directory name changes and F:\myproject directory for file name changes. The example uses the FindFirstChangeNotification() function to create two notification handles and the WaitForMultipleObjects() function to wait on the handles. Whenever a directory is created or deleted in the tree starting at F:\, the example updates the entire directory tree. Whenever a file is created or deleted in the F:\myproject directory, the example refreshes the directory.

// for WinXp

#define _WIN32_WINNT 0x0501

#include <windows.h>

#include <stdio.h>

 

int main(int argc, char argv[])

{

DWORD dwWaitStatus;

HANDLE dwChangeHandles[2];

char * DirName = "F:\\myproject"; // file

char * DirName1 = "F:\\";               // directory

 

// watch the F:\myproject directory for file creation and deletion.

dwChangeHandles[0] = FindFirstChangeNotification(

    DirName,                       // directory to watch

    FALSE,                         // do not watch the subtree

    FILE_NOTIFY_CHANGE_FILE_NAME); // watch file name changes

 

if (dwChangeHandles[0] == INVALID_HANDLE_VALUE)

       ExitProcess(GetLastError());

else

       printf("FindFirstChangeNotification() for file change is OK.\n");

 

// watch the F:\\ subtree for directory creation and deletion.

dwChangeHandles[1] = FindFirstChangeNotification(

    DirName1,                      // directory to watch

    TRUE,                          // watch the subtree

    FILE_NOTIFY_CHANGE_DIR_NAME);  // watch directory name changes

 

if (dwChangeHandles[1] == INVALID_HANDLE_VALUE)

    ExitProcess(GetLastError());

else

    printf("FindFirstChangeNotification() for directory change is OK.\n");

 

// again...some messages

if (dwChangeHandles[0] != INVALID_HANDLE_VALUE && dwChangeHandles[1] != INVALID_HANDLE_VALUE)

{

       printf("\nI'm monitoring any file deletion/creation in %s and\n", DirName);

       printf("I'm monitoring any directory deletion/creation in %s.\n", DirName1);

}

 

// change notification is set. Now wait on both notifications

// handles and refresh accordingly.

while (TRUE)

{

    // wait for notification.

    dwWaitStatus = WaitForMultipleObjects(2, dwChangeHandles, FALSE, INFINITE);

 

    switch (dwWaitStatus)

    {

        case 0: // WAIT_OBJECT_0

            // a file was created or deleted in F:\myproject.

            // refresh this directory and restart the change notification.

            // may call application define function here...

            if (FindNextChangeNotification(dwChangeHandles[0]) == FALSE)

                ExitProcess(GetLastError());

            else

               printf("File created/deleted in %s.\n", DirName);

            break;

 

        case 1: // WAIT_OBJECT_0 + 1

 

            // a directory was created or deleted in F:\.

            // refresh the directory tree and restart the change notification.

            // may call application define function here...

            if (FindNextChangeNotification(dwChangeHandles[1]) == FALSE)

                ExitProcess(GetLastError());

            else

               printf("Directory was deleted/created in %s.\n", DirName1);

            break;

            default:

              printf("FindNextChangeNotification(): Invalid return value.\n");

              ExitProcess(GetLastError());

       }

}

     // may close the handles...

     // FindCloseChangeNotification(dwChangeHandles[0]);

     // FindCloseChangeNotification(dwChangeHandles[1]);

     return 0;

}

To see the effect, firstly run the program (in this example the program was run at command prompt).  Next, create a directory in F:\, then create a file in F:\myproject and finally delete the directory and file that just created.  The output is shown below.

 

Windows Xp Pro and 2000 server family Process & Thread synchronization C program example: Wait objects

 

Waiting in a Message Loop

 

The following code fragment illustrates the use of the MsgWaitForMultipleObjects() function in a message loop.  This is C++ and MFC codes.

int MessageLoop (

    HANDLE* lphObjects,  // handles that need to be waited on

    int     cObjects     // number of handles to wait on

  )

{

    // the message loop lasts until we get a WM_QUIT message,

    // upon which we shall return from the function.

    while (TRUE)

    {

        // block-local variable

        DWORD result;

        MSG msg;

        // read all of the messages in this next loop,

        // removing each message as we read it.

        while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))

        {

            // if it is a quit message, exit.

            if (msg.message == WM_QUIT)

                return 1;

            // otherwise, dispatch the message.

            DispatchMessage(&msg);

        } // end of PeekMessage while loop.

        // wait for any message sent or posted to this queue

        // or for one of the passed handles be set to signaled.

        result = MsgWaitForMultipleObjects(cObjects, lphObjects, FALSE, INFINITE, QS_ALLINPUT);

        // the result tells us the type of event we have.

        if (result == (WAIT_OBJECT_0 + cObjects))

        {

            // new messages have arrived.

            // continue to the top of the always while loop to

            // dispatch them and resume waiting.

            continue;

        }

        else

        {

            // one of the handles became signaled.

            DoStuff (result – WAIT_OBJECT_0);

        } // end of else clause.

    } // end of the always while loop.

} // end of function.

Using Mutex Objects

 

You can use a mutex object to protect a shared resource from simultaneous access by multiple threads or processes. Each thread must wait for ownership of the mutex before it can execute the code that accesses the shared resource. For example, if several threads share access to a database, the threads can use a mutex object to permit only one thread at a time to write to the database.  In the following example, a process uses the CreateMutex() function to create a named mutex object or open a handle to an existing mutex object.

// for WinXp

#define _WIN32_WINNT 0x0501

#include <windows.h>

#include <stdio.h>

 

int main()

{

HANDLE hMutex;

// create a mutex with no initial owner...

hMutex = CreateMutex(

                       NULL,           // no security attributes

                       FALSE,          // initially not owned

                       "MutexToProtectSomeData"); // name of mutex

if (hMutex == NULL)

{

// check for error...

printf("CreateMutex() failed, error: %d.\n", GetLastError());

}

else

              printf("CreateMutex() is OK.\n");

return 0;

}

 

A sample output:

Process & Thread synchronization C program example: CreateMutex()  

 

When a thread of this process writes to the database, as in the next example, it first requests ownership of the mutex. If it gets ownership, the thread writes to the database and then releases its ownership.

The example uses structured exception-handling syntax to ensure that the thread properly releases the mutex object. The __finally block of code is executed no matter how the __try block terminates unless the __try block includes a call to the TerminateThread() function. This prevents the mutex object from being abandoned inadvertently.

// for WinXp

#define _WIN32_WINNT 0x0501

#include <windows.h>

#include <stdio.h>

 

BOOL FunctionToWriteSomeData(HANDLE hMutex)

{

    DWORD dwWaitResult;

     // request the ownership of mutex...

     dwWaitResult = WaitForSingleObject(

        hMutex,   // handle to mutex

        5000L);   // five-second time-out interval

    switch (dwWaitResult)

    {

        // the thread got the mutex ownership...

        case WAIT_OBJECT_0:

            // simple structured exception handling...

            __try {

                    printf("the mutex is signaled.\n");

                    // TODO: Write some data...

                   }

            __finally {

                // release the ownership of the mutex object.

                if (!ReleaseMutex(hMutex))

                {

                    // deal with error.

                    printf("ReleaseMutex() failed.\n");

                    ExitProcess(0);

                 }

                break;

            }

        // cannot get the mutex ownership due to time-out.

        case WAIT_TIMEOUT:

              printf("time-out interval elapsed, and the object's state is non-signaled.\n");

         return FALSE;

        // got the ownership of the abandoned mutex object.

        case WAIT_ABANDONED:

              printf("the mutex is set to non-signaled.\n");

              return FALSE;

    }

    return TRUE;

}

 

//===========================================================

int main()

{

       HANDLE hMutex;

       // create a mutex with no initial owner.

       hMutex = CreateMutex(

              NULL,                         // no security attributes

              FALSE,                      // initially not owned

              "MutexToProtectSomeData");  // name of mutex

       if (hMutex == NULL)

       {

// Check for error...

              printf("CreateMutex() failed, error: %d.\n", GetLastError());

       }

       else

              printf("CreateMutex() is OK.\n");

       // write some data function call...

       BOOL Test = FunctionToWriteSomeData(hMutex);

       // verify the returned value

       printf("The function returned value: %d.\n", Test);

       return 0;

}

 

A sample output:

Process & Thread synchronization C program example: More on mutex

 

Using Semaphore Objects

 

In the following example, a process uses a semaphore object to limit the number of windows it creates. First, it uses the CreateSemaphore() function to create the semaphore and to specify initial and maximum counts.

// for WinXp

#define _WIN32_WINNT 0x0501

#include <windows.h>

#include <stdio.h>

 

int main()

{

       HANDLE hSemaphore;

       LONG cMax = 15;

       // create a semaphore with initial and max. counts of 15...

       hSemaphore = CreateSemaphore(

              NULL,   // no security attributes

              cMax,   // initial count

              cMax,   // maximum count

              NULL);  // unnamed semaphore

       if (hSemaphore == NULL)

       {

              printf("CreateSemaphore() failed, error: %d.\n", GetLastError());

              // check for error...

       }

       else

              printf("CreateSemaphore() is OK.\n");

       return 0;

}

 

A sample output:

Process & Thread synchronization C program example: using semaphores

 

Before any thread of the process creates a new window, it uses the WaitForSingleObject() function to determine whether the semaphore’s current count permits the creation of additional windows. The wait function’s time-out parameter is set to zero, so the function returns immediately if the semaphore is non-signaled.

DWORD dwWaitResult;

// try to enter the semaphore gate...

dwWaitResult = WaitForSingleObject(

        hSemaphore,   // handle to semaphore

        0L);          // zero-second time-out interval

switch (dwWaitResult)

{

    // the semaphore object was signaled.

    case WAIT_OBJECT_0:

        // OK to open another window.

        break;

    // semaphore was non-signaled, so a time-out occurred.

    case WAIT_TIMEOUT:

        // cannot open another window...

        break;

}

When a thread closes a window, it uses the ReleaseSemaphore() function to increment the semaphore's count.

// increment the count of the semaphore...

if (!ReleaseSemaphore(

        hSemaphore,  // handle to semaphore

        1,                      // increase count by one

        NULL))             // not interested in previous count

{

    // deal with the error.

}

The following is a working program example demonstrating the use of semaphore object.

// for WinXp

#define _WIN32_WINNT 0x0501

#include <windows.h>

#include <stdio.h>

 

int main()

{

HANDLE hSemaphore;

LONG cMax = 10;

LONG cPreviousCount = 0;

// create a semaphore with initial and maximum counts of 10.

hSemaphore = CreateSemaphore(

    NULL,   // no security attributes

    cMax,    // initial count

    cMax,    // maximum count

    "MySemaphore");  // named semaphore

 

if (hSemaphore == NULL)

{

    // check for error.

    printf("CreateSemaphore() failed error: %d.\n", GetLastError());

}

else

    printf("CreateSemaphore() is OK.\n");

//==================================================================

DWORD dwWaitResult;

 

// try to enter the semaphore gate.

dwWaitResult = WaitForSingleObject(

        hSemaphore,   // handle to semaphore

        0L);                   // zero-second time-out interval

 

switch (dwWaitResult)

{

// the semaphore object was signaled.

case WAIT_OBJECT_0:

printf("The semaphore was signaled, do other task...\n");

// OK to open another window...

break;

case WAIT_ABANDONED:

printf("The semaphore was non-signaled, just exit.\n");

// Cannot open another window...

break;

// semaphore was non-signaled, so a time-out occurred.

case WAIT_TIMEOUT:

printf("The time-out interval elapsed, the semaphore was non-signaled, just exit.\n");

// cannot open another window.

break;

}

 

//==================================================================

 

// increment the count of the semaphore.

if (!ReleaseSemaphore(

        hSemaphore,  // handle to semaphore

        1,                      // increase count by one

        NULL))            // not interested in previous count

{

printf("ReleaseSemaphore() failed, error: %d.\n", GetLastError());

// deal with the error.

}

else

       printf("ReleaseSemaphore() is OK.\n");

 

       return 0;

}

 

A sample output:

Process & Thread synchronization C program example: Semaphore objects

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Further reading and digging:

 

  1. For Multibytes, Unicode characters and Localization please refer to Locale, wide characters & Unicode (Story) and Windows users & groups programming tutorials (Implementation).

  2. Microsoft Visual C++, online MSDN.

  3. Structure, enum, union and typedef story can be found C/C++ struct, enum, union & typedef.

  4. Check the best selling C / C++ and Windows books at Amazon.com.

 

 

 

 

|< Windows Processes & Threads: Synchronization 3 | Main | Windows Processes & Threads: Synchronization 5 >| Site Index | Download |


 

C & C++ Programming Tutorial | C Programming Practice