Using Event Objects
Using Critical Section Objects
Using Timer Queues
Using Waitable Timer Objects
Using Singly Linked Lists
Program examples on:
Using event objects.
Using critical section objects.
Using timer queues.
Using waitable timer objects.
Using singly linked lists.
My Training Period: xx hours. Before you begin, read someinstruction here. Functions and structure used in the program examples were dumped at Win32 functions & structures.
The Win32 programming abilities:
Note: For Multithreaded program examples, you have to set your project toMultithread project type.
Using Event Objects
Applications use event objects in a number of situations to notify a waiting thread of the occurrence of an event. For example, overlapped I/O operations on files, named pipes, and communications devices use an event object to signal their completion. In the following example, an application uses event objects to prevent several threads from reading from a shared memory buffer while a master thread is writing to that buffer. First, the master thread uses the CreateEvent() function to create a manual-reset event object. The master thread sets the event object to non-signaled when it is writing to the buffer and then resets the object to signaled when it has finished writing. Then it creates several reader threads and an auto-reset event object for each thread. Each reader thread sets its event object to signaled when it is not reading from the buffer.
#define NUMTHREADS 4
HANDLE hGlobalWriteEvent;
void CreateEventsAndThreads(void) { HANDLE hReadEvents[NUMTHREADS], hThread; DWORD i, IDThread; // create a manual-reset event object. The master thread sets this to non-signaled when it writes to the shared buffer. hGlobalWriteEvent = CreateEvent( NULL, // no security attributes TRUE, // manual-reset event TRUE, // initial state is signaled "MasterThWriteEvent" // object name ); if (hGlobalWriteEvent == NULL) { printf("CreateEvent() failed, error: %d.\n", GetLastError()); // error exit... } |
else
printf("CreateEvent(), master thread with manual reset is OK.\n\n");
// create multiple threads and an auto-reset event object for each thread. Each thread sets its event object to
// signaled when it is not reading from the shared buffer.
printf("Multiple threads with auto-reset event object for each thread...\n");
for (i = 0; i < NUMTHREADS; i++)
{
// create the auto-reset event.
hReadEvents[i] = CreateEvent(
NULL, // no security attributes
FALSE, // auto-reset event
TRUE, // initial state is signaled
NULL); // object not named
if (hReadEvents[i] == NULL)
{
printf("CreateEvent() failed.\n");
// error exit.
}
else
printf("CreateEvent() #%d is OK.\n", i);
hThread = CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE) ThreadFunction,
&hReadEvents[i], // pass event handle
0, &IDThread);
if (hThread == NULL)
{
// error exit.
printf("CreateThread() failed, error: %d.\n", GetLastError());
}
else
printf("CreateThread() #%d is OK.\n\n", i);
}
}
Before the master thread writes to the shared buffer, it uses the ResetEvent() function to set the state of hGlobalWriteEvent (an application-defined global variable) to non-signaled. This blocks the reader threads from starting a read operation. The master then uses the WaitForMultipleObjects() function to wait for all reader threads to finish any current read operations. When WaitForMultipleObjects() returns, the master thread can safely write to the buffer. After it has finished, it sets hGlobalWriteEvent and all the reader-thread events to signaled, enabling the reader threads to resume their read operations.
void WriteToBuffer(void)
{
DWORD dwWaitResult, i;
// reset hGlobalWriteEvent to non-signaled, to block readers.
if (!ResetEvent(hGlobalWriteEvent))
{
// error exit.
}
// wait for all reading threads to finish reading.
dwWaitResult = WaitForMultipleObjects(
NUMTHREADS, // number of handles in array
hReadEvents, // array of read-event handles
TRUE, // wait until all are signaled
INFINITE); // indefinite wait
switch (dwWaitResult)
{
// all read-event objects were signaled.
case WAIT_OBJECT_0:
// write to the shared buffer.
break;
// an error occurred.
default:
printf("Wait error: %d\n", GetLastError());
ExitProcess(0);
}
// set hGlobalWriteEvent to signaled.
if (!SetEvent(hGlobalWriteEvent))
{
// error exit.
}
// set all read events to signaled.
for (i = 0; i < NUMTHREADS; i++)
if (!SetEvent(hReadEvents[i]))
{
// error exit.
}
}
Before starting a read operation, each reader thread uses WaitForMultipleObjects() to wait for the application-defined global variable hGlobalWriteEvent and its own read event to be signaled. When WaitForMultipleObjects() returns, the reader thread's auto-reset event has been reset to non-signaled. This blocks the master thread from writing to the buffer until the reader thread uses the SetEvent() function to set the event's state back to signaled.
void ThreadFunction(LPVOID lpParam)
{
DWORD dwWaitResult;
HANDLE hEvents[2];
hEvents[0] = *(HANDLE*)lpParam; // thread's read event
hEvents[1] = hGlobalWriteEvent;
dwWaitResult = WaitForMultipleObjects(
2, // number of handles in array
hEvents, // array of event handles
TRUE, // wait till all are signaled
INFINITE); // indefinite wait
switch (dwWaitResult)
{
// both event objects were signaled.
case WAIT_OBJECT_0:
// read from the shared buffer.
break;
// an error occurred.
default:
printf("Wait error: %d\n", GetLastError());
ExitThread(0);
}
// set the read event to signaled.
if (!SetEvent(hEvents[0]))
{
// error exit.
}
}
To see some dummy action, run the following program example. It just a program skeleton because we don’t have the real read/write operation here. Don’t forget to run the program second time and see the output difference. Set your project to multithread type.
// For WinXp
#define _WIN32_WINNT 0x0501
#include <windows.h>
#include <stdio.h>
#define NUMTHREADS 4
HANDLE hGlobalWriteEvent;
HANDLE hReadEvents[NUMTHREADS], hThread;
DWORD i, IDThread;
void ThreadFunction(LPVOID lpParam)
{
DWORD dwWaitResult;
HANDLE hEvents[2];
hEvents[0] = *(HANDLE*)lpParam; // thread's read event
hEvents[1] = hGlobalWriteEvent;
dwWaitResult = WaitForMultipleObjects(
2, // number of handles in array
hEvents, // array of event handles
TRUE, // wait till all are signaled
INFINITE); // indefinite wait
printf("\nIn ThreadFunction()...\n");
switch (dwWaitResult)
{
// both event objects were signaled.
case WAIT_OBJECT_0:
printf("Both event objects were signaled.\n");
// read from the shared buffer...
break;
// an error occurred.
default:
printf("Wait error: %d\n", GetLastError());
ExitThread(0);
}
// set the read event to signaled.
if (!SetEvent(hEvents[0]))
{
printf("SetEvent(), setting the read event to signaled failed.\n");
// error exit.
}
else
printf("SetEvent(), setting the read event to signaled is OK.\n");
}
//===============================================================
void WriteToBuffer(void)
{
DWORD dwWaitResult, i;
printf("\nIn WriteToBuffer()...\n");
// reset hGlobalWriteEvent to non-signaled, to block readers...
if (!ResetEvent(hGlobalWriteEvent))
{
printf("ResetEvent(hGlobalWriteEvent) failed.\n");
// error exit.
}
else
printf("ResetEvent(hGlobalWriteEvent) is OK.\n");
// wait for all reading threads to finish reading...
dwWaitResult = WaitForMultipleObjects(
NUMTHREADS, // number of handles in array
hReadEvents, // array of read-event handles
TRUE, // wait until all are signaled
INFINITE); // indefinite wait
switch (dwWaitResult)
{
// all read-event objects were signaled...
case WAIT_OBJECT_0:
printf("All read-event objects were signaled.\n");
// write to the shared buffer...
break;
// an error occurred...
default:
printf("Wait error: %d\n", GetLastError());
ExitProcess(0);
}
// set hGlobalWriteEvent to signaled...
if (!SetEvent(hGlobalWriteEvent))
{
// error exit.
}
// set all read events to signaled...
for (i = 0; i < NUMTHREADS; i++)
{
if (!SetEvent(hReadEvents[i]))
{
printf("SetEvent(), setting read event %d to signaled failed.\n", i);
// error exit.
}
else
printf("SetEvent(), read event %d signaled.\n", i);
}
}
//===============================================================
void CreateEventsAndThreads(void)
{
// create a manual-reset event object. The master thread sets
// this to non-signaled when it writes to the shared buffer...
printf("In CreateEventsAndThreads()...\n");
hGlobalWriteEvent = CreateEvent(
NULL, // no security attributes
TRUE, // manual-reset event
TRUE, // initial state is signaled
// (LPCWSTR)"MasterThWriteEvent"
"MasterThWriteEvent" // object name
);
if (hGlobalWriteEvent == NULL)
{
printf("CreateEvent() failed, error: %d.\n", GetLastError());
// error exit...
}
else
printf("CreateEvent(), master thread with manual reset is OK.\n\n");
// create multiple threads and an auto-reset event object for each thread. Each thread sets its event object to
// signaled when it is not reading from the shared buffer...
printf("Multiple threads with auto-reset event object for each thread...\n");
for (i = 0; i < NUMTHREADS; i++)
{
// create the auto-reset event.
hReadEvents[i] = CreateEvent(
NULL, // no security attributes
FALSE, // auto-reset event
TRUE, // initial state is signaled
NULL); // object not named
if (hReadEvents[i] == NULL)
{
printf("CreateEvent() failed.\n");
// error exit.
}
else
printf("CreateEvent() #%d is OK.\n", i);
hThread = CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE) ThreadFunction,
&hReadEvents[i], // pass event handle
0, &IDThread);
if (hThread == NULL)
{
// error exit.
printf("CreateThread() failed, error: %d.\n", GetLastError());
}
else
printf("CreateThread() #%d is OK.\n\n", i);
}
}
int main()
{
CreateEventsAndThreads();
WriteToBuffer();
return 0;
}
A sample output:
|
In WriteToBuffer()...
ResetEvent(hGlobalWriteEvent) is OK.
All read-event objects were signaled.
In ThreadFunction()...
Both event objects were signaled.
SetEvent(), setting the read event to signaled is OK.
SetEvent(), read event 0 signaled.
In ThreadFunction()...
Both event objects were signaled.
SetEvent(), setting the read event to signaled is OK.
SetEvent(), read event 1 signaled.
In ThreadFunction()...
Both event objects were signaled.
SetEvent(), setting the read event to signaled is OK.
SetEvent(), read event 2 signaled.
In ThreadFunction()...
Both event objects were signaled.
SetEvent(), setting the read event to signaled is OK.
SetEvent(), read event 3 signaled.
Press any key to continue
The following output sample run using VC++ Express Edition with Windows SDK installed.
The following example shows how a thread initializes, enters, and releases a critical section. It uses the InitializeCriticalSectionAndSpinCount(),EnterCriticalSection(), LeaveCriticalSection(), and DeleteCriticalSection() functions.
#define _WIN32_WINNT 0x0501
#include <windows.h>
#include <stdio.h>
// global variable
CRITICAL_SECTION CriticalSection;
// application defined function...
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
// TODO: Other tasks...
printf("In ThreadProc(), application defined function...\n");
printf("EnterCriticalSection() and LeaveCriticalSection().\n");
// request ownership of the critical section.
EnterCriticalSection(&CriticalSection);
// TODO: For example, access the shared resource...
// release ownership of the critical section.
LeaveCriticalSection(&CriticalSection);
// TODO: Other tasks...
return 0;
}
int main()
{
// TODO: Other tasks...
printf("In main()...\n");
printf("InitializeCriticalSectionAndSpinCount() and after\n");
printf("return from function call use DeleteCriticalSection()...\n\n");
// initialize the critical section one time only...
if (!InitializeCriticalSectionAndSpinCount(&CriticalSection, 0x80000400))
printf("InitializeCriticalSectionAndSpinCount() failed, error: %d.\n", GetLastError());
// just return or other error processing...
else
printf("InitializeCriticalSectionAndSpinCount() is OK.\n");
// release resources used by the critical section object.
DeleteCriticalSection(&CriticalSection);
return 0;
}
A sample output:
The following example creates a timer routine that will be executed by a timer-queue thread after a 10 second delay. First, the code uses the CreateEvent() function to create an event object that is signaled when the timer-queue thread completes. Then it creates a timer queue and a timer-queue timer, using theCreateTimerQueue() and CreateTimerQueueTimer() functions, respectively. The code uses the WaitForSingleObject() function to determine when the timer routine has completed. Finally, the code calls DeleteTimerQueue() to clean up.
|
VOID CALLBACK TimerRoutine(PVOID lpParam, BOOL TimerOrWaitFired)
{
if (lpParam == NULL)
{
printf("TimerRoutine()'s lpParam is NULL.\n");
}
else
{
// lpParam points to the argument; in this case it is an int...
printf("Timer routine called. Parameter is %d.\n", *(int*)lpParam);
}
SetEvent(gDoneEvent);
}
int main()
{
HANDLE hTimer = NULL;
HANDLE hTimerQueue = NULL;
int arg = 123;
// use an event object to track the TimerRoutine() execution...
gDoneEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!gDoneEvent)
{
printf("CreateEvent() failed, error: %d.\n", GetLastError());
return 1;
}
else
printf("CreateEvent() is OK.\n");
// create the timer queue...
hTimerQueue = CreateTimerQueue();
if (!hTimerQueue)
{
printf("CreateTimerQueue() failed, error: %d.\n", GetLastError());
// may just return/exit with error code...
return 2;
}
else
printf("CreateTimerQueue() is OK.\n");
// set a timer to call the timer routine in 10 seconds...
if (!CreateTimerQueueTimer(&hTimer, hTimerQueue, (VOID CALLBACK)TimerRoutine, &arg, 10000, 0, 0))
{
printf("CreateTimerQueueTimer() failed, error: %d.\n", GetLastError());
// may just return/exit with error code...
return 3;
}
else
printf("CreateTimerQueueTimer() is OK and do the related task...\n");
// TODO: Do other useful work here...
printf("Call timer routine in 10 seconds...\n");
// wait for the timer-queue thread to complete using an event object. The thread will signal the event at that time...
if (WaitForSingleObject(gDoneEvent, INFINITE) != WAIT_OBJECT_0)
printf("WaitForSingleObject() failed, error: %d.\n", GetLastError());
else
printf("WaitForSingleObject() is OK.\n");
// delete all timers in the timer queue...
if (!DeleteTimerQueue(hTimerQueue))
printf("DeleteTimerQueue() failed, error: %d.\n", GetLastError());
else
printf("DeleteTimerQueue() is OK.\n");
return 0;
}
A sample output:
The following example creates a timer that will be signaled after a 10 second delay. First, the code uses the CreateWaitableTimer() function to create a waitable timer object. Then it uses the SetWaitableTimer() function to set the timer. The code uses the WaitForSingleObject() function to determine when the timer has been signaled.
#define _WIN32_WINNT 0x0501
#include <windows.h>
#include <stdio.h>
#include <windows.h>
#include <stdio.h>
int main()
{
HANDLE hTimer = NULL;
LARGE_INTEGER liDueTime;
liDueTime.QuadPart=-100000000;
// create a waitable timer.
hTimer = CreateWaitableTimer(NULL, TRUE, "WaitableTimer");
if (!hTimer)
{
printf("CreateWaitableTimer() failed, error: %d.\n", GetLastError());
return 1;
}
else
printf("CreateWaitableTimer() is OK.\n");
// next step...
printf("Waiting for 10 seconds...\n");
// set a timer to wait for 10 seconds.
if (!SetWaitableTimer(hTimer, &liDueTime, 0, NULL, NULL, 0))
{
printf("SetWaitableTimer() failed, error: %d.\n", GetLastError());
return 2;
}
else
printf("SetWaitableTimer() is OK.\n");
// wait for the timer.
if (WaitForSingleObject(hTimer, INFINITE) != WAIT_OBJECT_0)
printf("WaitForSingleObject() failed, error: %d.\n", GetLastError());
else
printf("Timer was signaled.\n");
return 0;
}
A sample output:
The following example uses the InitializeSListHead() function to initialize a singly linked list and the InterlockedPushEntrySList() function to insert 10 items. The example uses the InterlockedPopEntrySList() function to remove 10 items and the InterlockedFlushSList() function to verify that the list is empty.
#define _WIN32_WINNT 0x0501
#include <windows.h>
#include <stdio.h>
#include <malloc.h>
// structure to be used for a list item. Typically, the first member is of type SLIST_ENTRY. Additional members are used for data.
// here, the data is simply a signature for testing purposes.
typedefstruct _PROGRAM_ITEM {
SLIST_ENTRY ItemEntry;
ULONG Signature;
} PROGRAM_ITEM, *PPROGRAM_ITEM;
int main()
{
ULONG Count;
PSLIST_ENTRY FirstEntry, ListEntry;
SLIST_HEADER ListHead;
PPROGRAM_ITEM ProgramItem;
// initialize the list header.
InitializeSListHead(&ListHead);
// insert 10 items into the list.
for (Count = 1; Count <= 10; Count += 1)
{
ProgramItem = (PPROGRAM_ITEM)malloc(sizeof(*ProgramItem));
ProgramItem->Signature = Count;
FirstEntry = InterlockedPushEntrySList(&ListHead, &ProgramItem->ItemEntry);
}
// remove 10 items from the list.
for (Count = 10; Count >= 1; Count -= 1)
{
ListEntry = InterlockedPopEntrySList(&ListHead);
free(ListEntry);
}
// flush the list and verify that the items are gone.
ListEntry = InterlockedFlushSList(&ListHead);
FirstEntry = InterlockedPopEntrySList(&ListHead);
if (FirstEntry != NULL)
{
printf("InterlockedPopEntrySList() failed, error: List is not empty.");
}
else
printf("InterlockedPopEntrySList() is OK, list is empty.\n");
return 0;
}
A sample output:
Microsoft Visual C++, online MSDN.
Structure, enum, union and typedef story can be foundC/C++ struct, enum, union & typedef.
For Multibytes, Unicode characters and Localization please refer toLocale, wide characters & Unicode (Story) and Windows users & groups programming tutorials (Implementation).
Windows data types are Windows data types.
Check the best selling C / C++ and Windows books at Amazon.com.