lecture21_thread1

Post on 16-Jan-2016

213 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

DESCRIPTION

Lecture21_thread1

TRANSCRIPT

COMP 150-CCPCOMP 150-CCPConcurrent ProgrammingConcurrent Programming

Lecture 21:Concurrency in Other Environments (Part 1)

Dr. Richard S. Hall rickhall@cs.tufts.edu

Clement Escoffierclement.escoffier@gmail.com

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

top related