Threads CNS 3670
What is a thread? an independent unit of execution within a process a "lightweight process" an independent unit of execution within a process a "lightweight process"
How do threads relate to network programming? Chat client: One thread is always waiting for input from the server (via socket connection). One thread is always waiting for input from user Chat client: One thread is always waiting for input from the server (via socket connection). One thread is always waiting for input from user
Multi-threaded vs. Single-threaded Advantages of using more than one thread? Advantages of using only one thread? Advantages of using more than one thread? Advantages of using only one thread?
Multi-threaded vs. Single-threaded Advantages of using more than one thread: Single-threaded processes can't easily handle concurrent activities e.g., waiting for user I/O, network I/O, and doing calculations at the same time Better performance if more than one processor No guarantees Can sometimes get better performance even if there's only one CPU. How? Advantages of using more than one thread: Single-threaded processes can't easily handle concurrent activities e.g., waiting for user I/O, network I/O, and doing calculations at the same time Better performance if more than one processor No guarantees Can sometimes get better performance even if there's only one CPU. How?
Multi-threaded vs. Single-threaded Disadvantages of using more than one thread: Race conditions and deadlock One thread can accidentally modify another's resources. Have to deal with synchronization. C and C++ threads are not portable. Concurrency can lead to worse performance rather than better. Disadvantages of using more than one thread: Race conditions and deadlock One thread can accidentally modify another's resources. Have to deal with synchronization. C and C++ threads are not portable. Concurrency can lead to worse performance rather than better.
Multi-process vs. Multi-threaded Advantages of using more than one process? Advantages of using more than one thread? Advantages of using more than one process? Advantages of using more than one thread?
Advantages of multi-threading Less overhead Switching between threads is less expensive than switching between processes. Sharing resources between threads is easier than sharing between processes. Less overhead Switching between threads is less expensive than switching between processes. Sharing resources between threads is easier than sharing between processes.
Advantages of multi-processing Work with programs that you can't change Write different parts of system in different programming languages e.g., GUI for chat client Use more than one machine Work with programs that you can't change Write different parts of system in different programming languages e.g., GUI for chat client Use more than one machine
Thread Libraries Java threads built-in support for threads, synchronization pthreads: POSIX threads UNIX/Linux Windows threads ZThreads zthread.sourceforge.net portable C++ thread library Java threads built-in support for threads, synchronization pthreads: POSIX threads UNIX/Linux Windows threads ZThreads zthread.sourceforge.net portable C++ thread library
Creating a new thread Each thread has its own stack. Specify function that thread will begin to execute. Member functions in C++ are a bit tricky Pass a parameter (usually a pointer) With pthreads, the parameter is always a pointer of type void* Each thread has its own stack. Specify function that thread will begin to execute. Member functions in C++ are a bit tricky Pass a parameter (usually a pointer) With pthreads, the parameter is always a pointer of type void*
Windows CreateThread Win32 API Does not work right with the static C run- time library, can cause memory leaks _beginthread Closes thread handle _beginthreadex Recommended function for C, C++ In C run-time library CreateThread Win32 API Does not work right with the static C run- time library, can cause memory leaks _beginthread Closes thread handle _beginthreadex Recommended function for C, C++ In C run-time library
Threads with Visual C++ Link with LIBCMT.LIB and override default library #define _MT in all source files that use the C library Include for _beginthreadex and _endthreadex Create threads with _beginthreadex Terminate threads with _endthreadex or simply use a return statement at the end of the thread routine. Link with LIBCMT.LIB and override default library #define _MT in all source files that use the C library Include for _beginthreadex and _endthreadex Create threads with _beginthreadex Terminate threads with _endthreadex or simply use a return statement at the end of the thread routine. source: Windows System Programming, 3rd ed., by Johnson M. Hart, p. 207
_beginthreadex HANDLE hThread; unsigned threadID; hThread = (HANDLE)_beginthreadex( NULL, 0, &SecondThreadFunc, NULL, 0, &threadID ); HANDLE hThread; unsigned threadID; hThread = (HANDLE)_beginthreadex( NULL, 0, &SecondThreadFunc, NULL, 0, &threadID ); source: security attributes
_beginthreadex HANDLE hThread; unsigned threadID; hThread = (HANDLE)_beginthreadex( NULL, 0, &SecondThreadFunc, NULL, 0, &threadID ); HANDLE hThread; unsigned threadID; hThread = (HANDLE)_beginthreadex( NULL, 0, &SecondThreadFunc, NULL, 0, &threadID ); source: stack size: 0 for same as main thread
_beginthreadex HANDLE hThread; unsigned threadID; hThread = (HANDLE)_beginthreadex( NULL, 0, &SecondThreadFunc, NULL, 0, &threadID ); HANDLE hThread; unsigned threadID; hThread = (HANDLE)_beginthreadex( NULL, 0, &SecondThreadFunc, NULL, 0, &threadID ); source: start address: see next slide
unsigned Counter; unsigned __stdcall SecondThreadFunc( void* pArguments ) { printf( "In second thread…\n" ); while ( Counter < ) Counter++; _endthreadex( 0 ); return 0; } unsigned Counter; unsigned __stdcall SecondThreadFunc( void* pArguments ) { printf( "In second thread…\n" ); while ( Counter < ) Counter++; _endthreadex( 0 ); return 0; }
_beginthreadex HANDLE hThread; unsigned threadID; hThread = (HANDLE)_beginthreadex( NULL, 0, &SecondThreadFunc, NULL, 0, &threadID ); HANDLE hThread; unsigned threadID; hThread = (HANDLE)_beginthreadex( NULL, 0, &SecondThreadFunc, NULL, 0, &threadID ); source: pointer to argument(s)
_beginthreadex HANDLE hThread; unsigned threadID; hThread = (HANDLE)_beginthreadex( NULL, 0, &SecondThreadFunc, NULL, 0, &threadID ); HANDLE hThread; unsigned threadID; hThread = (HANDLE)_beginthreadex( NULL, 0, &SecondThreadFunc, NULL, 0, &threadID ); source: start state: 0 for running or CREATE_SUSPENDEND
_beginthreadex HANDLE hThread; unsigned threadID; hThread = (HANDLE)_beginthreadex( NULL, 0, &SecondThreadFunc, NULL, 0, &threadID ); HANDLE hThread; unsigned threadID; hThread = (HANDLE)_beginthreadex( NULL, 0, &SecondThreadFunc, NULL, 0, &threadID ); source: pointer for thread ID
Pitfalls Don’t make any assumptions about when threads will execute. Don’t use reasoning like “that will hardly ever happen”. Testing is necessary but not sufficient. Test on a variety of systems. Don’t make any assumptions about when threads will execute. Don’t use reasoning like “that will hardly ever happen”. Testing is necessary but not sufficient. Test on a variety of systems.
Pitfalls Only use threads when appropriate. “Fortunately, correct programs are frequently the simplest and have the most elegant design. Complexity should be avoided wherever possible.” Only use threads when appropriate. “Fortunately, correct programs are frequently the simplest and have the most elegant design. Complexity should be avoided wherever possible.” Windows System Programming, p. 223
Race Condition result of executing program(s) depends on who runs precisely when b = getBalance(“1234”); b += 500; setBalance(“1234”, b); b = getBalance(“1234”); b -= 100; setBalance(“1234”, b); Thread 1Thread 2 balance starts at 1000 What should the ending balance be? What will it be? Will getting rid of the local variable b solve the problem? How can we solve this problem?
Synchronization Need to prevent race conditions Critical region Part of a program that accesses shared memory (or another shared resource) To prevent race conditions, only allow one thread at a time to enter critical region Need to prevent race conditions Critical region Part of a program that accesses shared memory (or another shared resource) To prevent race conditions, only allow one thread at a time to enter critical region
volatile Optimizing compiler might leave a value in a register rather than writing it back to memory. Declare as volatile so that the variable will always be written to memory when changed and read from memory when accessed. Optimizing compiler might leave a value in a register rather than writing it back to memory. Declare as volatile so that the variable will always be written to memory when changed and read from memory when accessed.
Windows Synchronization CRITICAL_SECTION WaitForSingleObject File locks Mutexes Semaphores Events CRITICAL_SECTION WaitForSingleObject File locks Mutexes Semaphores Events
CRITICAL_SECTION Used to define a section of code that only one thread can execute at a time. Initialized and deleted, but do not have handles. Cannot be shared with other processes. Spend as little time as possible in CS. Used to define a section of code that only one thread can execute at a time. Initialized and deleted, but do not have handles. Cannot be shared with other processes. Spend as little time as possible in CS.
CRITICAL_SECTION CRITICAL_SECTION cs1; volatile DWORD N = 0; InitializeCriticalSection(&cs1);... EnterCriticalSection(&cs1); //...use N LeaveCriticalSection(&cs1);... DeleteCriticalSection(&cs1); Windows System Programming, p. 239
Mutexes Short for “mutual exclusion” Can be used by threads in separate processes Allow time-out values Become signaled when abandoned by a terminating process Short for “mutual exclusion” Can be used by threads in separate processes Allow time-out values Become signaled when abandoned by a terminating process
Mutex functions CreateMutex ReleaseMutex OpenMutex A thread locks a mutex by calling WaitForSingleObject or WaitForMultipleObjects CreateMutex ReleaseMutex OpenMutex A thread locks a mutex by calling WaitForSingleObject or WaitForMultipleObjects
Using a mutex Handle mutex; volatile DWORD N = 0; mutex = CreateMutex(NULL, false, “m1”);... WaitForSingleObject(mutex, INFINITE); //...use N ReleaseMutex(mutex);... CloseHandle(mutex); // The mutex will be destroyed when the // last handle is closed, or when the // process terminates. based on code from Windows System Programming, p. 248
Mutexes vs. CRITICAL_SECTIONs Abandoned mutexes are signaled Mutex waits can time out Mutexes can be shared with other processes You can use WaitForMultipleObjects with mutexes (avoid deadlock) Can get immediate ownership of mutex CSs are usually faster and simpler Abandoned mutexes are signaled Mutex waits can time out Mutexes can be shared with other processes You can use WaitForMultipleObjects with mutexes (avoid deadlock) Can get immediate ownership of mutex CSs are usually faster and simpler
Deadlock Circular wait e.g., Breakfasting Kindergarteners, pirate map Order access to resources All or nothing requests for resources Must get everything requested or nothing Circular wait e.g., Breakfasting Kindergarteners, pirate map Order access to resources All or nothing requests for resources Must get everything requested or nothing