lecture21_thread1
DESCRIPTION
Lecture21_thread1TRANSCRIPT
COMP 150-CCPCOMP 150-CCPConcurrent ProgrammingConcurrent Programming
Lecture 21:Concurrency in Other Environments (Part 1)
Dr. Richard S. Hall [email protected]
Clement [email protected]
Concurrent programming – April 8th, 2008
Why Use Java for the Course?Why Use Java for the Course?
Java is widely popular Java treats concurrency as a first-class concept
It is not tacked on as an afterthought It is reasonably simple to use the concurrency
mechanisms without having to deal with extra cruft
Java is available and works well across many platforms
Makes it accessible to students on their preferred computing environment
Why Not Java?Why Not Java?
Java is not suited to all tasks Other approaches are potentially more innovative
(e.g., Erlang)( Other languages are popular (e.g., C/C++)O Other platforms are popular (e.g., Linux,
Win32, .NET)W
The last two bullets will be the focus of this lecture to provide some perspective
AgendaAgenda
Linux, C/C++, and Pthreads Win32 and C/C++ .NET and C#
AgendaAgenda
Linux, C/C++, and PthreadsDerived from
https://computing.llnl.gov/tutorials/pthreads/
What are Pthreads?What are Pthreads?
Different platforms have their own proprietary versions of threads
Implementations differed substantially making it difficult to develop portable threaded application
A standardized threading API was required For UNIX-based systems, this interface has been
specified by the IEEE POSIX 1003.1c threads standard (aka Pthreads))
▴ A set of C language programming types and procedure calls
Pthreads RecommendationPthreads Recommendation
Be careful if your application uses libraries or other objects that do not explicitly guarantee thread safety
When in doubt, assume that they are not thread-safe until proven otherwise
This can be done by serializing the calls to the uncertain routine
Pthreads OverviewPthreads Overview
The subroutines which comprise the Pthreads API can be grouped into three major classes
Thread management▴ These functions work directly on threads▴ e.g., creating, detaching, and joining threads
Mutex management▴ These functions deal with synchronization▴ e.g., creating, destroying, locking and unlocking mutex locks
Condition variables▴ These functions address communication between threads
based on programmer specified conditions▴ e.g., creating, destroying, waiting and signaling condition
variables
Pthreads API Overview (1/2)Pthreads API Overview (1/2)PP
All library identifiers begin with pthread_ Common prefixes
▴ pthread_ - for threads themselves▴ pthread_attr_ - for thread attributes▴ pthread_mutex_ - for mutexes▴ pthread_mutexattr_ - for mutex attributes▴ pthread_cond_ - for condition variables▴ pthread_condattr_ - for condition variable attributes▴ pthread_key_ - for thread-specific data keys
We will concentrate on threads, mutexes, and condition variables
Pthreads API Overview (2/2)Pthreads API Overview (2/2)PP
Pthreads API is centered around the concept of opaque objects
The basic calls create or modify opaque objects
Pthreads API typically requires both resource creation and destruction to handle resource allocation
No garbage collector here
The Pthreads API contains over 60 subroutines The current POSIX standard is defined only for
the C language
Linux Development with PthreadsLinux Development with Pthreads
Include pthread.h in your source
Compile using “g++ -pthread”
Pthreads Thread APIPthreads Thread API
int pthread_create( pthread_t *restrict thread, const pthread_attr_t *restrict attr, void *(*start_routine)(void*), void *restrict arg);
int pthread_attr_init(pthread_attr_t *attr);
int pthread_attr_destroy(pthread_attr_t *attr);
int pthread_attr_getdetachstate( const pthread_attr_t *attr, int *detachstate);
int pthread_attr_setdetachstate( pthread_attr_t *attr, int detachstate);
int pthread_join(pthread_t thread, void **value_ptr);
int pthread_detach(pthread_t thread);
void pthread_exit(void *value_ptr);
Thread CreationThread Creation
pthread_create(thread,attr,start_routine,arg)p
Thread type: pthread_t Creates a new thread
Can create any number of new threads Created threads are peers in a given process
Arguments thread - opaque, unique thread identifier attr - opaque object for setting thread attributes start_routine - routine for the thread to execute arg - argument passed to the start routine
Thread AttributesThread Attributes
pthread_attr_init(attr);
pthread_attr_destroy(attr);
pthread_attr_getdetachstate(attr,detachstate);
pthread_attr_setdetachstate(attr,detachstate);
Create, set, get, and destroy thread attributes One specific attribute is whether a thread
joinable or detached Detached threads can never be joined Resources for joinable threads are freed when joined
Good practice to specifically create your threads as either joinable or detached
pthread_detach can detach a joinable thread
Thread TerminationThread Termination
pthread_exit(status);
Stops a thread Called when a thread is no longer needed Does not clean up resources Also needed in main() if it exits before threads
pthread_join(threadid,status);
Allows the calling thread to wait for the specified thread to exit
Retrieves the exit status from the exiting thread
Simple Thread Example (1/2)Simple Thread Example (1/2)SS
#include <pthread.h>#include <stdio.h>#include <stdlib.h>#define NUM_THREADS 3
// Routine for our threadsvoid *BusyWork(void *null) { int i; double result=0.0; // Perform busy work. for (i=0; i<1000000; i++) { result = result + (double)random(); } printf("result = %e\n",result); // Terminate the thread. pthread_exit((void *) 0);}
// Continued on next slide...
Simple Thread Example (2/2)Simple Thread Example (2/2)SS
int main () { pthread_t thread[NUM_THREADS]; pthread_attr_t attr; int t; void *status; // Create thread joinable attribute. pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); // Create the threads. for(t = 0; t < NUM_THREADS; t++) { printf("Creating thread %d\n", t); pthread_create(&thread[t], &attr, BusyWork, NULL); } // Destroy the attribute. pthread_attr_destroy(&attr); // Join all threads and print exit status. for(t = 0; t < NUM_THREADS; t++) { pthread_join(thread[t], &status); printf("Thread %d exit status = %ld\n", t, (long)status); } // Terminate main thread. pthread_exit(NULL);}
Miscellaneous Thread APIMiscellaneous Thread API
pthread_self()p Returns the identifier of the calling thread
pthread_equal(thread1,thread2)p Determines if two threads are equal
pthread_yield()p Forces the calling thread to give up the processor
Pthreads Mutex APIPthreads Mutex API
int pthread_mutex_init( pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
int pthread_mutexattr_init(pthread_mutexattr_t *attr);
int pthread_mutexattr_destroy( pthread_mutexattr_t *attr);
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
Mutex CreationMutex Creation
pthread_mutex_init(mutex,attr)p
Mutex type: pthread_mutex_t Mutexes are created in unlocked state Mutex attributes can control various properties,
but we don't explore these
Mutex Locking and UnlockingMutex Locking and Unlocking
pthread_mutex_lock(mutex);pthread_mutex_trylock(mutex);pthread_mutex_unlock(mutex);
Lock, test a lock, and unlock mutexes Mutexes are not reentrant
Trying to acquire a lock twice will deadlock
Mutexes are not magical, they are just an agreement between threads
If a thread doesn't use them, then all bets are off
Simple Mutex Example (1/2)Simple Mutex Example (1/2)SS
#include <pthread.h>#include <stdio.h>#include <stdlib.h>#define NUM_THREADS 3#define NUM_INCREMENTS 100
int shared; pthread_mutex_t mutex;
void *BusyWork(void *null) { int tmp; for (int i = 0; i < 100; i++) { // Lock. pthread_mutex_lock(&mutex); tmp = shared; tmp++; pthread_yield(); shared = tmp; // Unlock. pthread_mutex_unlock(&mutex); } // Terminate thread. pthread_exit((void *) 0);}// Continued on next slide...
Simple Mutex Example (2/2)Simple Mutex Example (2/2)SS
int main () { pthread_t thread[NUM_THREADS]; pthread_attr_t attr; // Create mutex. pthread_mutex_init(&mutex, NULL); // Create thread joinable attribute. pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); // Create the threads. for(int t = 0; t < NUM_THREADS; t++)f pthread_create(&thread[t], &attr, BusyWork, NULL); // Destroy the attribute. pthread_attr_destroy(&attr); // Join all threads. for(int t = 0; t < NUM_THREADS; t++)f pthread_join(thread[t], NULL); if (shared != (NUM_THREADS * NUM_INCREMENTS))i printf("Shared value is incorrect = %d\n", shared); // Destroy the mutex. pthread_mutex_destroy(&mutex); // Terminate main thread. pthread_exit(NULL);}
Pthreads Condition Variable APIPthreads Condition Variable API
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
int pthread_condattr_init(pthread_condattr_t *attr);
int pthread_condattr_destroy(pthread_condattr_t *attr);
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
int pthread_cond_timedwait( pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_destroy(pthread_cond_t *cond);
Condition Variable CreationCondition Variable Creation
pthread_cond_init(cond,attr);
Condition variable type: pthread_cond_t Condition variables allow threads to
communicate and wait for certain conditions Just like in Java
We don't explore condition variable attributes
Condition Variable WaitingCondition Variable Waiting
pthread_cond_wait(cond,mutex);pthread_cond_timedwait(
cond,mutex,abstime);
Must specify the mutex associated with the condition variable when waiting
Must be called when mutex is locked by calling thread
Can wait indefinitely or for a certain amount of time
Similar to Java, you should wait in a loop in case of a spurious wake up
Condition Variable SignalingCondition Variable Signaling
pthread_cond_signal(cond);pthread_cond_broadcast(cond);
Can signal a single thread or broadcast to all threads waiting on the condition variable
Must hold the lock to signal Should release the lock after signaling
Bounded Buffer Example (1/7)Bounded Buffer Example (1/7)BB
#include <iostream>#include <string>#include <pthread.h>
using namespace std;
class BoundedBuffer {private: // Encapsulate mutex and condition // variable. pthread_mutex_t mutex; pthread_cond_t condvar; string *buffer; int max, in, out, count;
public: BoundedBuffer(int m); ~BoundedBuffer(); void put(string s); string get();};
Bounded Buffer Example (2/7)Bounded Buffer Example (2/7)BB
BoundedBuffer::BoundedBuffer(int m) { max = m; buffer = new string[max]; in = out = count = 0; // Initialize mutex and condition variable. pthread_mutex_init(&mutex, NULL); pthread_cond_init(&condvar, NULL);}
BoundedBuffer::~BoundedBuffer() { // Destroy mutex and condition variable. pthread_mutex_destroy(&mutex); pthread_cond_destroy(&condvar);}
Bounded Buffer Example (3/7)Bounded Buffer Example (3/7)BB
void BoundedBuffer::put(string s) { // Acquire lock. pthread_mutex_lock(&mutex); // Wait for there to be space. while (count == max) { pthread_cond_wait(&condvar, &mutex); }
cout << "PUT "; for (int i = 0; i < count; i++) { cout << " " << buffer[(i + out) % max] << " "; } cout << "[" << s << "]" << endl; buffer[in] = s; ++count; in = (in + 1) % max;
// Broadcast and unlock. pthread_cond_broadcast(&condvar); pthread_mutex_unlock(&mutex);}
Bounded Buffer Example (4/7)Bounded Buffer Example (4/7)BB
string BoundedBuffer::get() { // Acquire lock. pthread_mutex_lock(&mutex); // Wait for data. while (count == 0) { pthread_cond_wait(&condvar, &mutex); }
string s = buffer[out]; buffer[out] = ""; --count; out = (out + 1) % max; cout << "GET [" << s << "] "; for (int i = 0; i < count; i++) { cout << buffer[(i + out) % max] << " "; } cout << endl;
// Broadcast and unlock. pthread_cond_broadcast(&condvar); pthread_mutex_unlock(&mutex); return s;}
Bounded Buffer Example (5/7)Bounded Buffer Example (5/7)BB
// Produce items to consume.void * producer(void *v) { BoundedBuffer *pBuffer = (BoundedBuffer *) v; string alphabet = "abcdefghijklmnopqrstuvwxyz"; int ai = 0; while (true) { pBuffer->put(alphabet.substr(ai, 1)); ai = (ai + 1) % 26; usleep(1000); }}
Bounded Buffer Example (6/7)Bounded Buffer Example (6/7)BB
// Consume produced items.void * consumer(void *v) { while (true) { BoundedBuffer *pBuffer = (BoundedBuffer *) v; string s = pBuffer->get(); usleep(1000); }}
Bounded Buffer Example (7/7)Bounded Buffer Example (7/7)BB
int main() { // Create shared buffer. BoundedBuffer *pBuffer = new BoundedBuffer(5);
// Create detached thread attribute. pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
// Create producer and consumer threads. pthread_t p, c; pthread_create(&p, &attr, producer, pBuffer); pthread_create(&c, &attr, consumer, pBuffer);
// Destroy attribute. pthread_attr_destroy(&attr);
// Terminate main thread. pthread_exit(NULL);}
Bounded Buffer IssueBounded Buffer Issue
No garbage collection in C/C++ makes some things more difficult
We would have to worry about freeing the allocated bounded buffer if the producer and consumer threads eventually terminate, but the program continues
▴ In Java we could just ignore this issue
Pthreads & C++Pthreads & C++
The Pthreads API was designed for C; however, integrating it with C++ is reasonably straightforward
The next few slides present a simple “thread” class in C++
http://www.oopweb.com/CPP/Documents/CPPHOWTO/Volume/C++Programming-HOWTO-18.html
Thread Class DefinitionThread Class Definition
class Thread{ public: Thread(); int Start(void *arg); protected: int Run(void *arg); static void * EntryPoint(void*); virtual void Setup(); virtual void Execute(void*); void * Arg() const { return arg_; } void Arg(void* a) { arg_ = a; } private: pthread_t threadId_; void * arg_;};
Thread Class ImplementationThread Class Implementation
Thread::Thread() {}
int Thread::Start(void *arg) { Arg(arg); // store user data int code = pthread_create( &threadId_, NULL, Thread::EntryPoint, this); return code;}
void * Thread::EntryPoint(void *pthis) { // static Thread * pthread = (Thread *) pthis; pthread->Run(Arg());}
int Thread::Run(void *arg) { Setup(); Execute(arg);}
virtual void Thread::Setup() { /* Override */ }
virtual void Thread::Execute(void *arg) { /* Override */ }
Thread Class UsageThread Class Usage
Each object is for a single thread Thread does not exist until Start() is called
To use, create a subclass The Setup() method can be overloaded to perform
any setup before the thread runs The Execute() method must be overloaded to
perform the function of the thread
Notice that the thread requires a static function We get around this by passing in this as the
argument to the thread, which casts it back to the Thread object so that it can invoke Run()R
Pthreads SummaryPthreads Summary
Pthreads gives us a portable threading API for UNIX-based systems
We only covered the basics in this lecture However, it is actually fairly simple and there isn't
much more
Pthreads is oriented toward C, but works reasonably well with C++
AgendaAgenda
Win32 and C/C++
Thread on WindowsThread on Windows
Very very useful Imagine a game without threads All Windows-based GUI use threads
Different thread libraries The old-fashion MFC (Microsoft Foundation Class) OpenThread (since Windows 98) New improvements in Vista
Example of Threads in WindowsExample of Threads in Windows
The windows explorer example Threads can be viewed with “Process Explorer” One “Browse” Thread per Windows Explorer
windows
Thread SchedulingThread Scheduling
Win32 threads are scheduled according to the round-robin principle
This includes multiprocessor machines Windows stays in control of how long each thread
gets executed
In a cycle, each thread gets allocated a ‘time slice’
Threads can have different priorities
Thread System OverviewThread System Overview
The Win32 Thread API can be grouped into three major classes
Thread management▴ These functions work directly on threads▴ e.g., creating, detaching, and joining threads
Mutex management▴ These functions deal with synchronization▴ e.g., creating, destroying, locking and unlocking mutex locks
Condition variables▴ These functions address communication between threads
based on programmer specified conditions▴ e.g., creating, destroying, waiting and signaling condition
variables
Thread LibraryThread Library
Header: include <windows.h>
It uses directly kernel32.h
Thread CreationThread Creation
HANDLE CreateThread(__in LPSECURITY_ATTRIBUTES lpThreadAttributes,__in DWORD dwStackSize,__in LPTHREAD_START_ROUTINE lpStartAddress,__in LPVOID lpParameter,__in DWORD dwCreationFlags,__out LPDWORD lpThreadIdjl
);
Arguments lpThreadAttributes – Security parameter ... NULL dwStackSize – Stack Size, can be NULL (default size set by Windows) lpStartAddress – Function pointer on you Thread run method lpParameter - The parameter to be passed to the thread procedure DwCreationFlags - The creation flags (0 or CREATE_SUSPENDED). LpThreadIdjl - [out] Address of the DWORD variable that, on
success, receives the thread ID of the newly created thread
Thread CreationThread Creation
Thread procedure Use a function pointer DWORD WINAPI ThreadProc( LPVOID
lpParameter );
Example of thread creationDWORD WINAPI ThreadProc(LPVOID lpParameter){
printf("This is my windows thread\n");}
int main(){DWORD threadID;CreateThread(NULL, 0, Thread1, NULL, 0, &threadID);Sleep(1000); // Avoid too fast terminationreturn 0;
}
Thread Creation with AttributesThread Creation with Attributes
DWORD WINAPI Thread1(int param) {printf("This is my thread n° %i \n", param);
}
int main() {DWORD threadID1;DWORD threadID2;DWORD threadID3;DWORD threadID4;CreateThread(NULL, 0, Thread1, 1, 0, &threadID1);CreateThread(NULL, 0, Thread1, 2, 0, &threadID2);CreateThread(NULL, 0, Thread1, 3, 0, &threadID3);CreateThread(NULL, 0, Thread1, 4, 0, &threadID4);Sleep(1000);return 0;
}
Stopping/Resuming ThreadsStopping/Resuming Threads
Function to stop the calling threadVOID WINAPI ExitThread( __in DWORD dwExitCode);
However, must be used carefully: The thread is exited before any destructors can be called or any other automatic
cleanup can be performed. Therefore, you should return from your thread function.
Function to resume a threadDWORD WINAPI ResumeThread(
__in HANDLE hThread);
Decrements a thread's suspend count. When the suspend count is decremented to zero, the execution of the thread is resumed.
Function to suspend a threadDWORD WINAPI ResumeThread(
__in HANDLE hThread);
Thread SynchronizationThread Synchronization
How to synchronized our threads: Events Mutexes Semaphores Critical Sections Condition Variables
Using EventsUsing Events
A thread wait for an event launched by another thread Allow to remove the sleep call
Create an event :HANDLE CreateEvent( __in LPSECURITY_ATTRIBUTES lpEventAttributes, // SD __in BOOL bManualReset, // reset type __in BOOL bInitialState, // initial state __in LPCTSTR lpName // object name);
Wait for an eventDWORD WaitForSingleObject( __in HANDLE hHandle, // handle to object __in DWORD dwMilliseconds // time-out interval);
Using EventsUsing Events
Signal event (becomes signaled):BOOL SetEvent( __in HANDLE hEvent // handle to event);
“Unsignal” event: BOOL ResetEvent( __in HANDLE hEvent // handle to event);
Others wait functions WaitForSingleObject(), WaitForMultipleObjects(),
SignalObjectAndWait(), WaitForMultipleObjectsEx()S MsgWaitForMultipleObjects(), MsgWaitForMultipleObjectsEx,
MsgWaitForMultipleObjectsEx(), SignalObjectAndWait(), WaitForMultipleObjectsEx(), WaitForSingleObjectEx()W RegisterWaitForSingleObject()R
Using EventsUsing Events
HANDLE g_event;
DWORD WINAPI ThreadProc(LPVOID lpParameter) {
printf("This is my thread\n");
SetEvent(g_event); // Once done set the event to signaled
}
int main() {
g_event=CreateEvent(NULL, 0, 0, NULL); // Create the event
DWORD threadID;
CreateThread(NULL, 0, Thread1, NULL, 0, &threadID);
WaitForSingleObject(g_event, INFINITE); // Wait for the event
return 0;
}
Using Mutex ObjectsUsing Mutex Objects
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.
HANDLE WINAPI CreateMutex( __in LPSECURITY_ATTRIBUTES lpMutexAttributes, __in BOOL bInitialOwner, __in_opt LPCTSTR lpName )
Getting the mutex by waiting for an event with WaitForSingleObject
Releasing a mutex withBOOL WINAPI ReleaseMutex(
__in HANDLE hMutex )
Using Mutex ObjectsUsing Mutex Objects
Main// Create a mutex with no initial owner
ghMutex = CreateMutex(
NULL, // default security attributes
FALSE, // initially not owned
NULL); // unnamed mutex );
In the thread procedure:dwWaitResult = WaitForSingleObject(
ghMutex, // handle to mutex
INFINITE); // no time-out interval
// Do something
ReleaseMutex(ghMutex)
Using SemaphoresUsing Semaphores
Very close to mutexesghSemaphore = CreateSemaphore(
NULL, // default security attributes
MAX_SEM_COUNT, // initial count
MAX_SEM_COUNT, // maximum count
NULL); // unnamed semaphore
dwWaitResult = WaitForSingleObject(
ghSemaphore, // handle to semaphore
INFINITE); // zero-second time-out interval
ReleaseSemaphore(
ghSemaphore, // handle to semaphore
1, // increase count by one
NULL) ) // not interested in previous count (out)
Using Critical SectionsUsing Critical Sections
Define a critical section by giving the entry address (pointer)InitializeCriticalSectionAndSpinCount(
&CriticalSection, //Pointer on the critical sections
0) // Spin count (0 on mono-processor)... // Release resources used by the critical section object. DeleteCriticalSection(&CriticalSection)
Then call entry and exit functions// Request ownership of the critical section. EnterCriticalSection(&CriticalSection);
// Access the shared resource.
// Release ownership of the critical section. LeaveCriticalSection(&CriticalSection);
Condition VariablesCondition Variables
Same semantic as in Java or Pthreads Four main functions
InitializeConditionVariable ▴ Initializes a condition variable.
SleepConidtionVariableCS ▴ Sleeps on the specified condition variable and releases the
specified critical section as an atomic operation. SleepConditionVariableSRW
▴ Sleeps on the specified condition variable and releases the specified SRW lock as an atomic operation.
WakeAllConditionVariable ▴ Wakes all threads waiting on the specified condition variable.
WakeConditionVariable▴ Wakes a single thread waiting on the specified condition variable.
Condition VariablesCondition Variables
The producer/consumer exampleEnterCriticalSection (&BufferLock);
while (QueueSize == BUFFER_SIZE && StopRequested == FALSE){
// Buffer is full - sleep so consumers can get items.
SleepConditionVariableCS (
&BufferNotFull, &BufferLock, INFINITE);
}
if (StopRequested == TRUE) {
LeaveCriticalSection (&BufferLock);
return; }
// Add the produced element …
WakeConditionVariable (&BufferNotEmpty);
LeaveCriticalSection (&BufferLock);
}
Other Synchronization MechanismsOther Synchronization Mechanisms
Waitable timer objects Waitable timers with an asynchronous procedure
call One-time initialization Using timer queues Atomic Actions
Win32 Threads IssuesWin32 Threads Issues
No garbage collection in C/C++ makes some things more difficult
Complex to use Do not forget to clean threads :
▴ All threads need to be stopped “nicely”▴ All handles need to be closed
ConclusionConclusion
Threads are by default supported in Win32 However, it exists others library
MFC (Microsoft Foundation Class) OpenThread (Close to Java thread)
To go further : MSDN : http://msdn.com