<Previous Lesson

Visual Programming

Next Lesson>

Lesson#26

Threads and Synchronization


Chapter 26
26.1 THREAD’S CREATION 2
26.2 THREAD’S EXAMPLE 4
26.2.1 THREAD PROCEDURE 4
26.3 SYNCHRONIZATION 5
26.3.1 OVERLAPPED INPUT AND OUTPUT 5
26.3.2 ASYNCHRONOUS PROCEDURE CALL 7
26.3.3 CRITICAL SECTION 7
26.4 WAIT FUNCTIONS 8
SINGLE-OBJECT WAIT FUNCTIONS 9
MULTIPLE-OBJECT WAIT FUNCTIONS 9
ALERTABLE WAIT FUNCTIONS
9
REGISTERED WAIT FUNCTIONS 10
WAIT FUNCTIONS AND SYNCHRONIZATION OBJECTS 10
WAIT FUNCTIONS AND CREATING WINDOWS 10
26.5 SYNCHRONIZATION OBJECTS 11
26.5.1 MUTEX OBJECT 12
26.6 THREAD EXAMPLE USING MUTEX OBJECT 14
26.7 CHECKING IF THE PREVIOUS APPLICATION IS RUNNING 14
26.8 EVENT OBJECT 15
26.8.1 USING EVENT OBJECT (EXAMPLE) 17
26.9 SEMAPHORE OBJECT 20
26.10 THREAD LOCAL STORAGE (TLS) 22
API IMPLEMENTATION FOR
TLS 22
C
OMPILER IMPLEMENTATION FOR TLS 22
SUMMARY 22
EXERCISES 22
Threads and Synchronization
2

26.1 Thread’s Creation

The CreateThread function creates a thread to execute within the virtual address space
of the calling process.
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES
lpThreadAttributes,
SIZE_T
dwStackSize,
LPTHREAD_START_ROUTINE
lpStartAddress,
LPVOID
lpParameter,
DWORD
dwCreationFlags,
LPDWORD
lpThreadId
);lpThreadAttributes: Pointer to a SECURITY_ATTRIBUTES structure that determines
whether the returned handle can be inherited by child processes. If lpThreadAttributes is
NULL, the handle cannot be inherited.
The lpSecurityDescriptor member of the structure specifies a security descriptor
for the new thread. If lpThreadAttributes is NULL, the thread gets a default
security descriptor. The ACLs in the default security descriptor for a thread come
from the primary or impersonation token of the creator.
dwStackSize: Initial size of the stack, in bytes. The system rounds this value to the nearest
page. If this parameter is zero, the new thread uses the default size for the executable.
lpStartAddress: Pointer to the application-defined function to be executed by the thread
and represents the starting address of the thread.
lpParameter: Pointer to a variable to be passed to the thread.
dwCreationFlags: Flags that control the creation of the thread. If the
CREATE_SUSPENDED flag is specified, the thread is created in a suspended state, and
will not run until the ResumeThread function is called. If this value is zero, the thread
runs immediately after creation.
lpThreadId: Pointer to a variable that receives the thread identifier. If this parameter is
NULL, the thread identifier is not returned.
Return value: If the function succeeds, the return value is a handle to the new thread.
If the function fails, the return value is NULL.
The number of threads a process can create is limited by the available virtual memory. By
default, every thread has one megabyte of stack space. Therefore, you can create at most
2028 threads. If you reduce the default stack size, you can create more threads. However,
Threads and Synchronization 3
your application will have better performance if you create one thread per processor and
build queues of requests for which the application maintains the context information. A
thread would process all requests in a queue before processing requests in the next queue.
The new thread handle is created with the THREAD_ALL_ACCESS access right. If a
security descriptor is not provided, the handle can be used in any function that requires a
thread object handle. When a security descriptor is provided, an access check is
performed on all subsequent uses of the handle before access is granted. If the access
check denies access, the requesting process cannot use the handle to gain access to the
thread. If the thread impersonates a client, then calls CreateThread with a NULL
security descriptor, the thread object created has a default security descriptor which
allows access only to the impersonation token's TokenDefaultDacl owner or members.
The thread execution begins at the function specified by the lpStartAddress parameter. If
this function returns, the DWORD return value is used to terminate the thread in an
implicit call to the ExitThread function. Use the GetExitCodeThread function to get
the thread's return value.
The thread is created with a thread priority of THREAD_PRIORITY_NORMAL. Use the
GetThreadPriority and SetThreadPriority functions to get and set the priority value of
a thread.
When a thread terminates, the thread object attains a signaled state, satisfying any threads
that were waiting on the object.
The thread object remains in the system until the thread has terminated and all handles to
it have been closed through a call to CloseHandle.
The ExitProcess, ExitThread, CreateThread, CreateRemoteThread functions, and a
process that is starting (as the result of a call by CreateProcess) are serialized between
each other within a process. Only one of these events can happen in an address space at a
time. This means that the following restrictions hold:
Do not create a thread while impersonating another user. The call will succeed, however
the newly created thread will have reduced access rights to itself when calling
GetCurrentThread. The access rights granted are derived from the access rights that the
impersonated user has to the process. Some access rights including
THREAD_SET_THREAD_TOKEN and THREAD_GET_CONTEXT may not be
present, leading to unexpected failures.
During process startup and DLL initialization routines, new threads can be
created, but they do not begin execution until DLL initialization is done for the
process.
Only one thread in a process can be in a DLL initialization or detach routine at a
time.
Threads and Synchronization 4
ExitProcess does not return until no threads are in their DLL initialization or
detach routines.
A thread that uses functions from the static C run-time libraries should use the
beginthread and endthread C run-time functions for thread management rather than
CreateThread and ExitThread. Failure to do so results in small memory leaks when
ExitThread is called. Note that this is not a problem with the C run-time in a DLL.

26.2 Thread’s Example

enum Shape { RECTANGLE, ELLIPSE };
DWORD WINAPI drawThread(LPVOID shape);
SYSTEMTIME st;
hThread1 = CreateThread(NULL, 0,
drawThread,
(LPVOID)RECTANGLE, CREATE_SUSPENDED,
&dwThread1
);
hThread2 = CreateThread(NULL, 0,
drawThread, (LPVOID)ELLIPSE,
CREATE_SUSPENDED, &dwThread2
);
hDC = GetDC(hWnd);
hBrushRectangle=CreateSolidBrush(RGB(170,220,160));
hBrushEllipse = CreateHatchBrush(HS_BDIAGONAL,RGB(175,180,225));
InitializeCriticalSection(&cs);
srand( (unsigned)time(NULL) );
ResumeThread(hThread2);
ResumeThread(hThread1);

26.2.1 Thread Procedure

DWORD WINAPI drawThread(LPVOID type)
{
int i;
if((enum Shape)type == RECTANGLE)
{
for(i=0; i<10000; ++i)
Threads and Synchronization 5
{
EnterCriticalSection(&cs);
SelectObject(hDC, hBrushRectangle);
Rectangle(hDC, 50, 1, rand()%300, rand()%100);
GetLocalTime(&st);
LeaveCriticalSection(&cs);
Sleep(10);
}
}

26.3 Synchronization

Using threads we can use lot of shared variables. These shared variables maybe used by
a single thread further more these variables may also be used and changed by several
parralle threads. If there are several threads operating at the same time then a particular
DC handle can be used in one of the threads only. If we want to use a single DC handle in
more then one thread, we use synchronization objects. Synchronization objects prevent
other threads to use the shared data at the same.
To synchronize access to a resource, use one of the synchronization objects in one of the
wait functions. The state of a synchronization object is either signaled or nonsignaled.
The wait functions allow a thread to block its own execution until a specified nonsignaled
object is set to the signaled state.

26.3.1 Overlapped Input and Output

You can perform either synchronous or asynchronous (or overlapped) I/O operations on
files, named pipes, and serial communications devices. The WriteFile, ReadFile,

DeviceIoControl, WaitCommEvent, ConnectNamedPipe, and TransactNamedPipe

functions can be performed either synchronously or asynchronously. The ReadFileEx and WriteFileEx functions can be performed asynchronously only.
When a function is executed synchronously, it does not return until the operation has
been completed. This means that the execution of the calling thread can be blocked for an
indefinite period while it waits for a time-consuming operation to finish. Functions called
for overlapped operation can return immediately, even though the operation has not been
completed. This enables a time-consuming I/O operation to be executed in the
background while the calling thread is free to perform other tasks. For example, a single
thread can perform simultaneous I/O operations on different handles, or even
simultaneous read and write operations on the same handle.
To synchronize its execution with the completion of the overlapped operation, the calling
thread uses the GetOverlappedResult function or one of the wait functions to determine
when the overlapped operation has been completed. You can also use the
HasOverlappedIoCompleted macro to poll for completion.
Threads and Synchronization 6
To cancel all pending asynchronous I/O operations, use the CancelIo function. This
function only cancels operations issued by the calling thread for the specified file handle.
Overlapped operations require a file, named pipe, or communications device that was
created with the FILE_FLAG_OVERLAPPED flag. To call a function to perform an
overlapped operation, the calling thread must specify a pointer to an OVERLAPPEDstructure. If this pointer is NULL, the function return value may incorrectly indicate that
the operation completed. The system sets the state of the event object to nonsignaled
when a call to the I/O function returns before the operation has been completed. The
system sets the state of the event object to signaled when the operation has been
completed.
When a function is called to perform an overlapped operation, it is possible that the
operation will be completed before the function returns. When this happens, the results
are handled as if the operation had been performed synchronously. If the operation was
not completed, however, the function's return value is FALSE, and the GetLastError function returns ERROR_IO_PENDING.
A thread can manage overlapped operations by either of two methods:
Use the GetOverlappedResult function to wait for the overlapped operation to
be completed.
Specify a handle to the OVERLAPPED structure's manual-reset event object in
one of the wait functions and then call GetOverlappedResult after the wait
function returns. The GetOverlappedResult function returns the results of the
completed overlapped operation, and for functions in which such information is
appropriate, it reports the actual number of bytes that were transferred.
When performing multiple simultaneous overlapped operations, the calling thread must
specify an OVERLAPPED structure with a different manual-reset event object for each
operation. To wait for any one of the overlapped operations to be completed, the thread
specifies all the manual-reset event handles as wait criteria in one of the multiple-object
wait functions. The return value of the multiple-object wait function indicates which
manual-reset event object was signaled, so the thread can determine which overlapped
operation caused the wait operation to be completed.
If no event object is specified in the OVERLAPPED structure, the system signals the
state of the file, named pipe, or communications device when the overlapped operation
has been completed. Thus, you can specify these handles as synchronization objects in a
wait function, though their use for this purpose can be difficult to manage. When
performing simultaneous overlapped operations on the same file, named pipe, or
communications device, there is no way to know which operation caused the object's
state to be signaled. It is safer to use a separate event object for each overlapped
operation.
Threads and Synchronization 7

26.3.2 Asynchronous Procedure Call

An asynchronous procedure call (APC) is a function that executes asynchronously in the
context of a particular thread. When an APC is queued to a thread, the system issues a
software interrupt. The next time the thread is scheduled, it will run the APC function.
APCs made by the system are called "kernel-mode APCs." APCs made by an application
are called "user-mode APCs." A thread must be in an alertable state to run a user-mode
APC.
Each thread has its own APC queue. An application queues an APC to a thread by calling
the QueueUserAPC function. The calling thread specifies the address of an APC
function in the call to QueueUserAPC. The queuing of an APC is a request for the
thread to call the APC function.
When a user-mode APC is queued, the thread to which it is queued is not directed to call
the APC function unless it is in an alertable state. A thread enters an alertable state when
it calls the SleepEx, SignalObjectAndWait, MsgWaitForMultipleObjectsEx,
WaitForMultipleObjectsEx, or WaitForSingleObjectEx function. Note that you
cannot use WaitForSingleObjectEx to wait on the handle to the object for which the
APC is queued. Otherwise, when the asynchronous operation is completed, the handle is
set to the signaled state and the thread is no longer in an alertable wait state, so the APC
function will not be executed. However, the APC is still queued, so the APC function will
be executed if you call another alertable wait function.
Note that the ReadFileEx, SetWaitableTimer, and WriteFileEx functions are
implemented using an APC as the completion notification callback mechanism.

26.3.3 Critical Section

Critical section objects provide synchronization similar to that provided by mutex
objects, except that 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 (a processor-specific test and set
instruction). 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. Unlike a
mutex object, there is no way to tell whether a critical section has been abandoned.
The process is responsible for allocating the memory used by a critical section. Typically,
this is done by simply declaring a variable of type CRITICAL_SECTION. Before the
threads of the process can use it, initialize the critical section by using the
InitializeCriticalSection or InitializeCriticalSectionAndSpinCount function.
Threads and Synchronization 8
A thread uses the EnterCriticalSection or TryEnterCriticalSection function to request
ownership of a critical section. It uses the LeaveCriticalSection function to release
ownership of a critical section. If the critical section object is currently owned by another
thread, EnterCriticalSection waits indefinitely for ownership. In contrast, when a mutex
object is used for mutual exclusion, the wait functions accept a specified time-out
interval. The TryEnterCriticalSection function attempts to enter a critical section
without blocking the calling thread.
Once a thread owns a critical section, it can make additional calls to
EnterCriticalSection or TryEnterCriticalSection without blocking its execution. This
prevents a thread from deadlocking itself while waiting for a critical section that it
already owns. To release its ownership, the thread must call LeaveCriticalSection once
for each time that it entered the critical section.
A thread uses the InitializeCriticalSectionAndSpinCount or
SetCriticalSectionSpinCount function to specify a spin count for the critical section
object. On single-processor systems, the spin count is ignored and the critical section spin
count is set to 0. On multiprocessor systems, if the critical section is unavailable, the
calling thread will spin dwSpinCount times before performing a wait operation on a
semaphore associated with the critical section. If the critical section becomes free during
the spin operation, the calling thread avoids the wait operation.
Any thread of the process can use the DeleteCriticalSection function to release the
system resources that were allocated when the critical section object was initialized. After
this function has been called, the critical section object can no longer be used for
synchronization.
When a critical section object is owned, the only other threads affected are those waiting
for ownership in a call to EnterCriticalSection. Threads that are not waiting are free to
continue running.

26.4 Wait Functions

The wait functions to allow a thread to block its own execution. The wait functions do not
return until the specified criteria have been met. The type of wait function determines the
set of criteria used. When a wait function is called, it checks whether the wait criteria
have been met. If the criteria have not been met, the calling thread enters the wait state. It
uses no processor time while waiting for the criteria to be met.
There are four types of wait functions:
single-object
multiple-object
alertable
registered
Threads and Synchronization 9

Single-object Wait Functions

The SignalObjectAndWait, WaitForSingleObject, and WaitForSingleObjectEx functions require a handle to one synchronization object. These functions return when
one of the following occurs:
The specified object is in the signaled state.
The time-out interval elapses. The time-out interval can be set to INFINITE to
specify that the wait will not time out.
The SignalObjectAndWait function enables the calling thread to atomically set the state
of an object to signaled and wait for the state of another object to be set to signaled.

Multiple-object Wait Functions

The WaitForMultipleObjects, WaitForMultipleObjectsEx,
MsgWaitForMultipleObjects
, and MsgWaitForMultipleObjectsEx functions enable
the calling thread to specify an array containing one or more synchronization object
handles. These functions return when one of the following occurs:
The state of any one of the specified objects is set to signaled or the states of all
objects have been set to signaled. You control whether one or all of the states will
be used in the function call.
The time-out interval elapses. The time-out interval can be set to INFINITE to
specify that the wait will not time out.
The MsgWaitForMultipleObjects and MsgWaitForMultipleObjectsEx function allow
you to specify input event objects in the object handle array. This is done when you
specify the type of input to wait for in the thread's input queue.
For example, a thread could use MsgWaitForMultipleObjects to block its execution
until the state of a specified object has been set to signaled and there is mouse input
available in the thread's input queue. The thread can use the GetMessage or
PeekMessage function to retrieve the input.
When waiting for the states of all objects to be set to signaled, these multiple-object
functions do not modify the states of the specified objects until the states of all objects
have been set signaled. For example, the state of a mutex object can be signaled, but the
calling thread does not get ownership until the states of the other objects specified in the
array have also been set to signaled. In the meantime, some other thread may get
ownership of the mutex object, thereby setting its state to nonsignaled.

Alertable Wait Functions

The MsgWaitForMultipleObjectsEx, SignalObjectAndWait,
WaitForMultipleObjectsEx, and WaitForSingleObjectEx functions differ from the
Threads and Synchronization 10
other wait functions in that they can optionally perform an alertable wait operation. In an
alertable wait operation, the function can return when the specified conditions are met,
but it can also return if the system queues an I/O completion routine or an APC for
execution by the waiting thread. For more information about alertable wait operations
and I/O completion routines. See Synchronization and Overlapped Input and Output. For
more information about APCs, see Asynchronous Procedure Calls that is already
described in our above section Synchronization.

Registered Wait Functions

The RegisterWaitForSingleObject function differs from the other wait functions in that
the wait operation is performed by a thread from the thread pool. When the specified
conditions are met, the callback function is executed by a worker thread from the thread
pool.
By default, a registered wait operation is a multiple-wait operation. The system resets the
timer every time the event is signaled (or the time-out interval elapses) until you call the
UnregisterWaitEx function to cancel the operation. To specify that a wait operation
should be executed only once, set the dwFlags parameter of
RegisterWaitForSingleObject to WT_EXECUTEONLYONCE.
Wait Functions and Synchronization ObjectsThe wait functions can modify the states of some types of synchronization objects.
Modification occurs only for the object or objects whose signaled state caused the
function to return. Wait functions can modify the states of synchronization objects as
follows:
The count of a semaphore object decreases by one, and the state of the semaphore
is set to nonsignaled if its count is zero.
The states of mutex, auto-reset event, and change-notification objects are set to
nonsignaled.
The state of a synchronization timer is set to nonsignaled.
The states of manual-reset event, manual-reset timer, process, thread, and console
input objects are not affected by a wait function.

Wait Functions and Creating Windows

You have to be careful when using the wait functions and code that directly or indirectly
creates windows. If a thread creates any windows, it must process messages. Message
broadcasts are sent to all windows in the system. If you have a thread that uses a wait
function with no time-out interval, the system will deadlock. Two examples of code that
indirectly creates windows are DDE and COM CoInitialize. Therefore, if you have a
thread that creates windows, use MsgWaitForMultipleObjects or
MsgWaitForMultipleObjectsEx, rather than the other wait functions.
Threads and Synchronization 11

26.5 Synchronization Objects

A synchronization object is an object whose handle can be specified in one of the wait
functions
to coordinate the execution of multiple threads. More than one process can have
a handle to the same synchronization object, making interprocess synchronization
possible.
The following object types are provided exclusively for synchronization.

Type Description

Event Notifies one or more waiting threads that an event has occurred.
Mutex Can be owned by only one thread at a time, enabling threads to coordinate
mutually exclusive access to a shared resource.
Semaphore Maintains a count between zero and some maximum value, limiting the
number of threads that are simultaneously accessing a shared resource.
Waitable
timer Notifies one or more waiting threads that a specified time has arrived.
Though available for other uses, the following objects can also be used for
synchronization.

Object Description

Change
notification
Created by the FindFirstChangeNotification function, its state is set to
signaled when a specified type of change occurs within a specified
directory or directory tree.
Console input
Created when a console is created. The handle to console input is returned
by the CreateFile function when CONIN$ is specified, or by the
GetStdHandle function. Its state is set to signaled when there is unread
input in the console's input buffer, and set to nonsignaled when the input
buffer is empty.
Job
Created by calling the CreateJobObject function. The state of a job object
is set to signaled when all its processes are terminated because the
specified end-of-job time limit has been exceeded.
Memory
resource
notification
Created by the CreateMemoryResourceNotification function. Its state is
set to signaled when a specified type of change occurs within physical
memory.
Process
Created by calling the CreateProcess function. Its state is set to
nonsignaled while the process is running, and set to signaled when the
process terminates.
Thread
Created when a new thread is created by calling the CreateProcess,
CreateThread, or CreateRemoteThread function. Its state is set to
nonsignaled while the thread is running, and set to signaled when the
thread terminates.
Threads and Synchronization 12
In some circumstances, you can also use a file, named pipe, or communications device as
a synchronization object; however, their use for this purpose is discouraged. Instead, use
asynchronous I/O and wait on the event object set in the OVERLAPPED structure. It is
safer to use the event object because of the confusion that can occur when multiple
simultaneous overlapped operations are performed on the same file, named pipe, or
communications device. In this situation, there is no way to know which operation caused
the object's state to be signaled.

26.5.1 Mutex Object

The CreateMutex function creates or opens a named or unnamed mutex object.
HANDLE CreateMutex(
LPSECURITY_ATTRIBUTES
lpMutexAttributes,/*null if default security
attributes*/
BOOL
bInitialOwner, /*is the current thread is the initialize owner*/
LPCTSTR
lpName /*name of the named mutex object*/

);

lpMutexAttributes: Pointer to a
SECURITY_ATTRIBUTES structure that determines
whether the returned handle can be inherited by child processes. If lpMutexAttributes is
NULL, the handle cannot be inherited.
The lpSecurityDescriptor member of the structure specifies a security descriptor
for the new mutex. If lpMutexAttributes is NULL, the mutex gets a default
security descriptor. The ACLs in the default security descriptor for a mutex come
from the primary or impersonation token of the creator.
bInitialOwner: If this value is TRUE and the caller created the mutex, the calling thread
obtains initial ownership of the mutex object. Otherwise, the calling thread does not
obtain ownership of the mutex.
lpName: Pointer to a null-terminated string specifying the name of the mutex object. The
name is limited to MAX_PATH characters. Name comparison is case sensitive.
If lpName matches the name of an existing named mutex object, this function
requests the MUTEX_ALL_ACCESS access right. In this case, the bInitialOwner
parameter is ignored because it has already been set by the creating process. If the
lpMutexAttributes parameter is not NULL, it determines whether the handle can
be inherited, but its security-descriptor member is ignored.
If lpName is NULL, the mutex object is created without a name.
If lpName matches the name of an existing event, semaphore, waitable timer, job,
or file-mapping object, the function fails and the GetLastError function returns
ERROR_INVALID_HANDLE. This occurs because these objects share the same
name space.
Threads and Synchronization 13
Terminal Services: The name can have a "Global\" or "Local\" prefix to
explicitly create the object in the global or session name space. The remainder of
the name can contain any character except the backslash character (\).
Return Values:
If the function succeeds, the return value is a handle to the mutex object. If the
named mutex object existed before the function call, the function returns a handle
to the existing object and GetLastError returns ERROR_ALREADY_EXISTS.
Otherwise, the caller created the mutex.
The handle returned by CreateMutex has the MUTEX_ALL_ACCESS access right and
can be used in any function that requires a handle to a mutex object.
Any thread of the calling process can specify the mutex-object handle in a call to one of
the wait functions. The single-object wait functions return when the state of the specified
object is signaled. The multiple-object wait functions can be instructed to return either
when any one or when all of the specified objects are signaled. When a wait function
returns, the waiting thread is released to continue its execution.
The state of a mutex object is signaled when it is not owned by any thread. The creating
thread can use the bInitialOwner flag to request immediate ownership of the mutex.
Otherwise, a thread must use one of the wait functions to request ownership. When the
mutex's state is signaled, one waiting thread is granted ownership, the mutex's state
changes to nonsignaled, and the wait function returns. Only one thread can own a mutex
at any given time. The owning thread uses the ReleaseMutex function to release its
ownership.
The thread that owns a mutex can specify the same mutex in repeated wait function calls
without blocking its execution. Typically, you would not wait repeatedly for the same
mutex, but this mechanism prevents a thread from deadlocking itself while waiting for a
mutex that it already owns. However, to release its ownership, the thread must call
ReleaseMutex once for each time that the mutex satisfied a wait.
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.
Threads and Synchronization 14
A process can specify the mutex-object handle in a call to the DuplicateHandlefunction 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.
Use the CloseHandle function to close the handle. The system closes the handle
automatically when the process terminates. The mutex object is destroyed when its last
handle has been closed.

26.6 Thread Example Using Mutex Object

hThread1= CreateThread(NULL, 0, drawThread, (LPVOID)RECTANGLE,
CREATE_SUSPENDED, &dwThread1);
hThread2 = .. .. ..
hBrushRectangle = CreateSolidBrush(RGB(170,220,160));
hBrushEllipse=CreateHatchBrush(HS_BDIAGONAL,RGB(175,180,225));
hMutex=CreateMutex(NULL, 0, NULL);
srand( (unsigned)time(NULL) );
ResumeThread(hThread2);
for(i=0; i<10000; ++i)
{
Switch(WaitForSingleObject(hMutex, INFINITE))
{
case WAIT_OBJECT_0:
SelectObject(hDC, hBrushRectangle);
Rectangle(hDC, 50, 1, rand()%300, rand()%100);
GetLocalTime(&st);
ReleaseMutex(hMutex);
Sleep(10);
};

26.7 Checking if the previous application is running

Using Named Mutex object you can check the application instance whether it is already
running or not. Recreating the named mutex open the previous mutex object but set last
error to ERROR_ALREADY_EXIST. You can check GetLastError if it is
ERROR_ALREADY_EXIST, then it is already running.
Threads and Synchronization 15

26.8 Event Object

The CreateEvent function creates or opens a named or unnamed event object.
HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES
lpEventAttributes, /*null for the default
security */
BOOL
bManualReset, /*manual reset or automatically reset its state*/
BOOL
bInitialState, /*set initialize state signaled or unsignalled*/
LPCTSTR
lpName /* nanme of the event object*/
);lpEventAttributes: Pointer to a SECURITY_ATTRIBUTES structure that determines
whether the returned handle can be inherited by child processes. If lpEventAttributes is
NULL, the handle cannot be inherited.
The lpSecurityDescriptor member of the structure specifies a security descriptor
for the new event. If lpEventAttributes is NULL, the event gets a default security
descriptor. The ACLs in the default security descriptor for an event come from the
primary or impersonation token of the creator.
bManualReset: If this parameter is TRUE, the function creates a manual-reset event
object which requires use of the ResetEvent function set the state to nonsignaled. If this
parameter is FALSE, the function creates an auto-reset event object, and system
automatically resets the state to nonsignaled after a single waiting thread has been
released.
bInitialState: If this parameter is TRUE, the initial state of the event object is signaled;
otherwise, it is nonsignaled.
lpName: Pointer to a null-terminated string specifying the name of the event object. The
name is limited to MAX_PATH characters. Name comparison is case sensitive.
If lpName matches the name of an existing named event object, this function
requests the EVENT_ALL_ACCESS access right. In this case, the bManualReset
and bInitialState parameters are ignored because they have already been set by the
creating process. If the lpEventAttributes parameter is not NULL, it determines
whether the handle can be inherited, but its security-descriptor member is ignored.
If lpName is NULL, the event object is created without a name.
If lpName matches the name of an existing semaphore, mutex, waitable timer, job,
or file-mapping object, the function fails and the GetLastError function returns
ERROR_INVALID_HANDLE. This occurs because these objects share the same
name space.
Threads and Synchronization 16
Return Values:
If the function succeeds, the return value is a handle to the event object. If the
named event object existed before the function call, the function returns a handle
to the existing object and GetLastError returns ERROR_ALREADY_EXISTS.
The handle returned by CreateEvent has the EVENT_ALL_ACCESS access right and
can be used in any function that requires a handle to an event object.
Any thread of the calling process can specify the event-object handle in a call to one of
the wait functions. The single-object wait functions return when the state of the specified
object is signaled. The multiple-object wait functions can be instructed to return either
when any one or when all of the specified objects are signaled. When a wait function
returns, the waiting thread is released to continue its execution.
The initial state of the event object is specified by the bInitialState parameter. Use the
SetEvent function to set the state of an event object to signaled. Use the ResetEventfunction to reset the state of an event object to nonsignaled.
When the state of a manual-reset event object is signaled, it remains signaled until it is
explicitly reset to nonsignaled by the ResetEvent function. Any number of waiting
threads, or threads that subsequently begin wait operations for the specified event object,
can be released while the object's state is signaled.
When the state of an auto-reset event object is signaled, it remains signaled until a single
waiting thread is released; the system then automatically resets the state to nonsignaled. If
no threads are waiting, the event object's state remains signaled.
Multiple processes can have handles of the same event 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 an
event object if the lpEventAttributes parameter of CreateEvent enabled
inheritance.
A process can specify the event-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 an event object in a call to the OpenEvent or
CreateEvent function.
Use the CloseHandle function to close the handle. The system closes the handle
automatically when the process terminates. The event object is destroyed when its last
handle has been closed.
Threads and Synchronization 17
26.8.1 Using Event Object (Example)
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 nonsignaled when it writes to the shared buffer.
hGlobalWriteEvent = CreateEvent(
NULL, // no security attributes
TRUE, // manual-reset event
TRUE, // initial state is signaled
"WriteEvent" // object name
);
if (hGlobalWriteEvent == NULL)
{
// error exit
}
// 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.
for(i = 1; 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)
{
Threads and Synchronization 18
// Error exit.
}
hThread = CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE) ThreadFunction,
&hReadEvents[i], // pass event handle
0, &IDThread);
if (hThread == NULL)
{
// Error exit.
}
}
}
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
nonsignaled. 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 nonsignaled, 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);
}
Threads and Synchronization 19
// Set hGlobalWriteEvent to signaled.
if (! SetEvent(hGlobalWriteEvent) )
{
// Error exit.
}
// Set all read events to signaled.
for(i = 1; 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 autoreset
event has been reset to nonsignaled. 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.
Threads and Synchronization 20
}
}

26.9 Semaphore Object

The CreateSemaphore function creates or opens a named or unnamed semaphore object.
HANDLE CreateSemaphore(
LPSECURITY_ATTRIBUTES
lpSemaphoreAttributes,
LONG
lInitialCount,
LONG
lMaximumCount,
LPCTSTR
lpName
);lpSemaphoreAttributes: Pointer to a SECURITY_ATTRIBUTES structure that
determines whether the returned handle can be inherited by child processes. If
lpSemaphoreAttributes is NULL, the handle cannot be inherited.
The lpSecurityDescriptor member of the structure specifies a security descriptor
for the new semaphore. If lpSemaphoreAttributes is NULL, the semaphore gets a
default security descriptor. The ACLs in the default security descriptor for a
semaphore come from the primary or impersonation token of the creator.
lInitialCount: Initial count for the semaphore object. This value must be greater than or
equal to zero and less than or equal to lMaximumCount. The state of a semaphore is
signaled when its count is greater than zero and nonsignaled when it is zero. The count is
decreased by one whenever a wait function releases a thread that was waiting for the
semaphore. The count is increased by a specified amount by calling the
ReleaseSemaphore function.
lMaximumCount: Maximum count for the semaphore object. This value must be greater
than zero.
lpName: Pointer to a null-terminated string specifying the name of the semaphore object.
The name is limited to MAX_PATH characters. Name comparison is case sensitive.
If lpName matches the name of an existing named semaphore object, this function
requests the SEMAPHORE_ALL_ACCESS access right. In this case, the
lInitialCount and lMaximumCount parameters are ignored because they have
already been set by the creating process. If the lpSemaphoreAttributes parameter
is not NULL, it determines whether the handle can be inherited, but its securitydescriptor
member is ignored.
If lpName is NULL, the semaphore object is created without a name.
If lpName matches the name of an existing event, mutex, waitable timer, job, or
file-mapping object, the function fails and the GetLastError function returns
Threads and Synchronization 21
ERROR_INVALID_HANDLE. This occurs because these objects share the same
name space.
Return Values:
If the function succeeds, the return value is a handle to the semaphore object. If
the named semaphore object existed before the function call, the function returns
a handle to the existing object and GetLastError returns
ERROR_ALREADY_EXISTS.
If the function fails, the return value is NULL. To get extended error information,
call GetLastError.
The handle returned by CreateSemaphore has the SEMAPHORE_ALL_ACCESS
access right and can be used in any function that requires a handle to a semaphore object.
Any thread of the calling process can specify the semaphore-object handle in a call to one
of the wait functions. The single-object wait functions return when the state of the
specified object is signaled. The multiple-object wait functions can be instructed to return
either when any one or when all of the specified objects are signaled. When a wait
function returns, the waiting thread is released to continue its execution.
The state of a semaphore object is signaled when its count is greater than zero, and
nonsignaled when its count is equal to zero. The lInitialCount parameter specifies the
initial count. Each time a waiting thread is released because of the semaphore's signaled
state, the count of the semaphore is decreased by one. Use the
ReleaseSemaphorefunction to increment a semaphore's
count by a specified amount. The count can never be
less than zero or greater than the value specified in the lMaximumCount parameter.
Multiple processes can have handles of the same semaphore 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
semaphore object if the lpSemaphoreAttributes parameter of CreateSemaphore enabled inheritance.
A process can specify the semaphore-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 semaphore object in a call to the
OpenSemaphore or CreateSemaphore function.
Use the CloseHandle function to close the handle. The system closes the handle
automatically when the process terminates. The semaphore object is destroyed when its
last handle has been closed.
Threads and Synchronization 22

26.10 Thread Local Storage (TLS)

Thread Local Storage (TLS) is the method by which each thread in a given multithreaded
process may allocate locations in which to store thread-specific data. Dynamically bound
(run-time) thread-specific data is supported by way of the TLS API (TlsAlloc,
TlsGetValue, TlsSetValue, TlsFree). Win32 and the Visual C++ compiler, now support
statically bound (load-time) per-thread data in addition to the existing API
implementation.

API Implementation for TLS

Thread Local Storage is implemented through the Win32 API layer as well as the
compiler. For details, see the Win32 API documentation for TlsAlloc, TlsGetValue,
TlsSetValue, and TlsFree.
The Visual C++ compiler includes a keyword to make thread local storage operations
more automatic, rather than through the API layer. This syntax is described in the next
section, Compiler Implementation for TLS.

Compiler Implementation for TLS

To support TLS, a new attribute, thread, has been added to the C and C++ languages and
is supported by the Visual C++ compiler. This attribute is an extended storage class
modifier, as described in the previous section. Use the __declspec keyword to declare a
thread variable. For example, the following code declares an integer thread local variable
and initializes it with a value:
__declspec( thread ) int tls_i = 1;

Summary

In this lecture, we studied about Threads and synchronization. To synchronize
access to a resource, use one of the synchronization objects in one of the wait functions.
The state of a synchronization object is either signaled or nonsignaled. The wait
functions allow a thread to block its own execution until a specified nonsignaled object is
set to the signaled state. Critical section objects provide synchronization similar to that
provided by mutex objects, except that 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. Another synchronization object is semaphore, events and mutex. Threads
with synchronization problems have the best use in network applications.

Exercises

1. Create Thread to find factorial of any number.

<Previous Lesson

Visual Programming

Next Lesson>

Home

Lesson Plan

Topics

Go to Top

Next Lesson
Previous Lesson
Lesson Plan
Topics
Home
Go to Top