650 likes | 790 Views
Windows Thread Management. Objectives and Benefits. Describe Windows thread management Use threads in Windows applications Use threads with C library functions Build and execute threaded applications Describe the various Windows synchronization mechanisms
E N D
Objectives and Benefits • Describe Windows thread management • Use threads in Windows applications • Use threads with C library functions • Build and execute threaded applications • Describe the various Windows synchronization mechanisms • Differentiate synchronization object features and how to select between them • Use synchronization in Windows applications
Threads: Benefits and Risks • Benefits • Simpler program models • Faster code – in many cases • Exploit multiple processors • Exploit inherent application parallelism • Reliable, understandable, maintainable code • Risks • Slower performance – in some cases • Potential defects
Contents • Process and Thread Overview • Thread Management • Waiting for Thread Termination • The C Library and Threads • Demonstration: Building a Threaded Application • Need for Synchronization • Thread Synchronization Objects • CRITICAL_SECTIONs (Deadlock Example) • Mutexes for Mutual Exclusion • Events • Semaphores
1. Process and Thread Overview • Threads in a process share data and code • Each thread has its own stack for function calls • Calling thread can pass an argument to a thread at creation time • This argument is on the stack • Each thread can allocate its own Thread Local Storage (TLS) indices and set TLS values • Threads are scheduled and run independently • The executive schedules threads • Threads run asynchronously • Threads can be preempted • Or restarted at any time
Processes and Threads Process Code Global Variables Process Heap Process Resources Open Files Heaps… Environment Block Thread 1 Thread N Thread Local Storage Thread Local Storage ... Stack Stack
Threads Performing Parallel Tasks Single-Threaded Program Multithreaded Program Thread 1 Thread 2 Read File A Read File A Read File B Read File B Wait forThread 1 andThread 2 to finish Thread 3 Merge datafrom both files Merge datafrom both files Thread 3 Reading File B beforeFile A would give the same results
2. Thread Management • Creating a Thread • The Thread Function • Thread Termination • Thread Exit Codes • Thread Identities • Suspending and Resuming Threads
Creating a Thread (1/4) • Specify the thread’s start address within the process’ code • Specify the stack size, and the stack consumes space within the process’ address space • The stack cannot be expanded • Specify a pointer to an argument for the thread • Can be nearly anything • Interpreted by the thread itself • CreateThread returns a thread’s ID value and its handle • A NULL handle value indicates failure
Creating a Thread (2/4) HANDLE CreateThread ( LPSECURITY_ATTRIBUTES lpsa, DWORD cbStack, LPTHREAD_START_ROUTINE lpStartAddr, LPVOID lpvThreadParm, DWORD dwCreate, LPDWORD lpIDThread )
Creating a Thread (3/4) • Parameters • lpsa • Security attributes structure (use NULL) • cbStack • Byte size for the new thread’s stack • Use 0 to default to the primary thread’s stack size (1 MB) • lpStartAddr • Points to the function (within the calling process) to be executed • Accepts a single pointer argument and returns a 32-bit DWORD exit code • The thread can interpret the argument as a DWORD or a pointer • lpThreadParm • The pointer passed as the thread argument
Creating a Thread (4/4) • dwCreate • If zero, the thread is immediately ready to run • If CREATE_SUSPENDED, the new thread will be in the suspended state, requiring a ResumeThread function call to move the thread to the ready state • lpIDThread • Points to a DWORD that receives the new thread’s identifier; NULL OK on W2000/NT
The Thread Function DWORD WINAPI MyThreadFunc ( PVOID pThParam ) { . . . ExitThread (ExitCode); /* OR */ return ExitCode; }
Thread Termination (1/2) • Threads are terminated by ExitProcess • The process and all its threads terminate • The exit code returned by the thread start function same as the process exit code • Or a thread can simply return with its exit code • ExitThread is the preferred technique • The thread’s stack is deallocated on termination • VOID ExitThread (DWORD (dwExitCode) • When the last thread in a process terminates, so does the process itself
Thread Termination (2/2) • You can terminate a different thread with TerminateThread • Dangerous: The thread’s stack and other resources will not be deallocated • Better to let the thread terminate itself • A thread will remain in the system until the last handle to it is closed (using CloseHandle) • Then the thread will be deleted • Any other thread can retrieve the exit code
Thread Exit Codes • BOOL GetExitCodeThread ( • HANDLE hThread, • LPDWORD lpdwExitCode ) • lpdwExitCode • Contains the thread’s exit code • It could be STILL_ACTIVE
Thread Identities • A thread has a permanent “ThreadId” • A thread is usually accessed by HANDLE • An ID can be converted to a HANDLE • HANDLE GetCurrentThread (VOID); • DWORD GetCurrentThreadId (VOID); • HANDLE OpenThread ( • DWORD dwDesiredAccess, • BOOL InheritableHandle, • DWORD ThreadId ); • /* >= Windows 2000 only */
Suspend & Resume Threads (1/2) • Every thread has a suspend count • A thread can execute only if this count is zero • A thread can be created in the suspended state • One thread can increment or decrement the suspend count of another: • DWORD ResumeThread (HANDLE hThread)
Suspend & Resume Threads (2/2) • DWORD SuspendThread (HANDLE hThread) • Both functions return previous suspend count • 0xFFFFFFFF indicates failure • Useful in preventing “race conditions” • Do not allow threads to start until initialization is complete • Unsafe for general synchronization
Waiting for Thread Termination • Wait for a thread to terminate using general purpose wait functions • WaitForSingleObjector WaitForMultipleObjects • Using thread handles • The wait functions wait for the thread handle to become signaled • Thread handle is signaled when thread terminates • ExitThread and TerminateThread set the object to the signaled state • Releasing all other threads waiting on the object • ExitProcess sets the process’ state and all its threads’ states to signaled
The Wait Functions (1/2) • DWORD WaitForSingleObject ( • HANDLE hObject, • DWORD dwTimeOut )
The Wait Functions (2/2) • DWORD WaitForMultipleObjects ( • DWORD cObjects, • LPHANDLE lphObjects, • BOOL fWaitAll, • DWORD dwTimeOut ) • Return: The cause of the wait completion
Wait Options (1/2) • Specify either a single handle hObject • Or an array of cObjects referenced by lphObjects • cObjects should not exceed MAXIMUM_WAIT_OBJECTS - 64
Wait Options (2/2) • dwTimeOut is in milliseconds • 0 means the function returns immediately after testing the state of the specified objects • Use INFINITE for no timeout • Wait forever for a thread to terminate • GetExitCodeThread • Returns the thread exit code
Wait Function Return Values (1/2) • fWaitAll • If TRUE, wait for all threads to terminate Possible return values are: • WAIT_OBJECT_0 • The thread terminated (if callingWaitForMultipleObjects;fWaitAll set)
Wait Function Return Values (2/2) • WAIT_OBJECT_0 + n where 0 <= n < cObjects • Subtract WAIT_OBJECT_0 from the return value to determine which thread terminated when calling WaitForMultipleObjects with fWaitAll set • WAIT_TIMEOUT • Timeout period elapsed • WAIT_ABANDONED • Not possible with thread handles • WAIT_FAILED • CallGetLastError for thread-specific error code
The C Library and Threads • Nearly all programs (and thread functions) use the C library • But the normal C library is not “thread safe” • The C function _beginthreadex has exactly the same parameters as CreateThread
Using _beginthreadex • Cast the _beginthreadex return value to (HANDLE) • Use _endthreadex in place of ExitThread • #include <process.h> • Set the multithreaded environment as follows: • #define _MT in every source file before <windows.h> • Link with LIBCMT.LIB • Override the default library • Preferred method using Visual C++ • From the menu bar: • Build Settings — C/C++ Tab • Code Generation category • Select a multithreaded run-time library
Ex: A Simple Boss Thread HANDLE hWork[K]; volatile LONGLONG WorkDone[K], iTh; /* !! */ . . . for (iTh = 0; iTh < K; iTh++) { WorkDone[ith] = 0; hWork[iTh] = _beginthreadex (NULL, 0, WorkTh, (PVOID)&iTh, 0, NULL); /* BUG! */ } WaitForMultipleObjects (K, hWork, TRUE, INFINITE); for (iTh = 0; iTh < K; iTh++) printf (“Thread %d did %d workunits\n”, iTh, WorkDone[iTh]);
Ex: A Simple Worker Thread DWORD WINAPI WorkTh (PVOID pThNum) { DWORD ThNum = (DWORD)(*pThNum); while (. . .) { /* Perform work */ WorkDone[ThNum]++; } _endthreadex (0); }
A SYNCHRONIZATION PROBLEM Thread 1 Thread 2 M N Running Ready 4 4 5 M = N; M = M + 1; Running Ready M = N; M = M + 1; N = M; 4 5 5 Running Ready N = M; 5 · · · · · ·
2. Thread Synchronization Objects • Known Windows mechanism to synchronize threads: • A thread can wait for another to terminate (using ExitThread) by waiting on the thread handle using WaitForSingleObject or WaitForMultipleObjects • A process can wait for another process to terminate (ExitProcess) in the same way • Other common methods (not covered here): • Reading from a pipe or socket that allows one process or thread to wait for another to write to the pipe (socket) • File locks are specifically for synchronizing file access
Synchronization Objects • Windows provides four other objects specifically designed for thread and process synchronization • Three are kernel objects (they have HANDLEs) • Events • Semaphores • Mutexes • Many inherently complex problems – beware: • Deadlocks • Race conditions • Missed signals • Many more
Critical Section Objects • Critical sections • Fourth object type can only synchronize threads within a process • Often the most efficient choice • Apply to many application scenarios • “Fast mutexes” • Not kernel objects • Critical section objects are initialized, not created • Deleted, not closed • Threads enter and leave critical sections • Only 1 thread at a time can be in a specific critical section • There is no handle — there is a CRITICAL_SECTION type
3. CRITICAL_SECTIONs • VOID InitializeCriticalSection ( • LPCRITICAL_SECTION lpcsCriticalSection) • VOID DeleteCriticalSection ( • LPCRITICAL_SECTION lpcsCriticalSection) • VOID EnterCriticalSection ( • LPCRITICAL_SECTION lpcsCriticalSection)
CRITICAL_SECTION Management • VOID LeaveCriticalSection ( • LPCRITICAL_SECTION lpcsCriticalSection) • BOOL TryCriticalSection ( • LPCRITICAL_SECTION lpcsCriticalSection)
CRITICAL_SECTIONS Usage • EnterCriticalSection blocks a thread if another thread is in (“owns”) the section • Use TryCriticalSection to avoid blocking • A thread can enter a CS more than once (“recursive”) • The waiting thread unblocks when the “owning” thread executes LeaveCriticalSection • A thread must leave a CS once for every time it entered • Common usage: allow threads to access global variables • Don’t forget to declare these variables volatile!
SYNCHRONIZATION CSs Thread 1 Thread 2 M Running N Idle 4 ECS(&CS); M = N; M = M + 1; 4 5 Idle Running ECS(&CS); Running Blocked N = M; 5 LCS (&CS); Running · · · M = N; M = M + 1; N = M; 5 6 6 LCS(&CS); · · ·
CRITICAL_SECTION Comments • CRITICAL_SECTIONS test in user-space • Fast – no kernel call • But wait in kernel space • Usually faster than Mutexes – See Session 3 • But, not always • Factors include number of threads, number of processors, and amount of thread contention • Performance can sometimes be “tuned” • Adjust the “spin count” – more later • CSs operate using polling and the equivalent of interlocked functions
4. Deadlock Example • Here is a program defect that could cause a deadlock • Some function calls are abbreviated for brevity • Aways enter CSs in the same order; leave in reverse order • Deadlocks are specific kind of race condition • To avoid deadlocks, create a lock hierarchy
Deadlock Example CRITICAL_SECTION csM, csN; volatile DWORD M = 0, N = 0; ... InitializeCriticalSection (&csM); ICS (&csN); ... DWORD ThreadFunc (...) { ECS (&csM); ECS (&csN); M = ++N; N = M - 2; LCS (&csN); LCS (&csM); ... ECS (&csM); ECS (&csN); M = N--; N = M + 2; LCS (&csN); LCS (&csM); }
6. Mutexes for Mutual Exclusion • Mutexes can be named and have HANDLEs • They are kernel objects • They can be used for interprocess synchronization • They are owned by a thread rather than a process • Threads gain mutex ownership by waiting on mutex handle • With WaitForSingleObject or WaitForMultipleObjects • Threads release ownership with ReleaseMutex
Mutexes (cont’d) • Recursive: A thread can acquire a specific mutex several times but must release the mutex the same number of times • Can be convenient, for example, with nested transactions • You can poll a mutex to avoid blocking • A mutex becomes “abandoned” if its owning thread terminates • Events and semaphores are the other kernel objects • Very similar life cycle and usage
Mutexes (cont’d) • HANDLE CreateMutex (LPSECURITY_ATTRIBUTES lpsa, • BOOL fInitialOwner, • LPCTSTR lpszMutexName) • The fInitialOwner flag, if TRUE, gives the calling thread immediate ownership of the new mutex • It is overridden if the named mutex already exists • lpszMutexNamepoints to a null-terminated pathname • Pathnames are case sensitive • Mutexes are unnamed if the parameter is NULL
Mutexes (cont’d) • BOOL ReleaseMutex (HANDLE hMutex) • ReleaseMutex frees a mutex that the calling thread owns • Fails if the thread does not own it • If a mutex is abandoned, a wait will return WAIT_ABANDONED_0 • This is the base value on a wait multiple • OpenMutex opens an existing named mutex • Allows threads in different processes to synchronize
Mutexes (cont’d) • Mutex naming: • Name a mutex that is to be used by more than one process • Mutexes, semaphores, & events share the same name space • Memory mapping objects also use this name space • So do waitable timers • Not necessary to name a mutex used in a single process
Mutexes (cont’d) • Process interaction with a named mutex Process 1 h = CreateMutex ("MName"); Process 2 h = OpenMutex ("MName");
7. EVENTS (1 of 6) • Events can release multiple threads from a wait simultaneously when a single event is signaled • A manual-reset event can signal several threads simultaneously and must be reset by the thread • An auto-reset event signals a single thread, and the event is reset automatically • Signal an event with either PulseEvent or SetEvent • Four combinations with very different behavior • Be careful! There are numerous subtle problems • Recommendation: Only use with SignalObjectAndWait(0) • (Much) more on this later – Chapter 9
EVENTS (2 of 6) • HANDLE CreateEvent ( • LPSECURITY_ATTRIBUTES lpsa, • BOOL fManualReset, BOOL fInitialState, • LPTCSTR lpszEventName) • Manual-reset event: set fManualReset to TRUE • Event is initially set to signaled if fInitialState is TRUE • Open a named event with OpenEvent • Possibly from another process
EVENTS (3 of 6) • The three functions for controlling events are: BOOL SetEvent (HANDLE hEvent) BOOL ResetEvent (HANDLE hEvent) BOOL PulseEvent (HANDLE hEvent)