1 / 59

Threads

Threads. Progressing With Parallel Processing (PWPP?) eWeek (09/18/06) Vol. 23, No. 37, P. D5

Télécharger la présentation

Threads

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Threads

  2. Progressing With Parallel Processing (PWPP?) eWeek (09/18/06) Vol. 23, No. 37, P. D5 Multithreading skills are becoming essential as parallel processing hardware proliferates, and developers ignore at their own peril indications of this trend such as Intel's investments in college curriculum and resources for multithread development training. The Java programming language supports the expression of concurrency in the same language developers are already employing for application logic, while powerful abstractions for C++ are also offered by concurrency toolkits and frameworks. Being able to count the threads developers are using on the fingers of one hand is folly, according to principal author of "Java Concurrency in Practice" Brian Goetz. He and his five co-authors note that "The need for thread safety is contagious," because "frameworks may create threads on your behalf, and code called from these threads must be thread-safe.“ It is the authors' contention that developers should never lose sight of the application state and avoid becoming overwhelmed by threading mechanisms. Developers must also keep in mind that careless habits that are acceptable in single-thread environments may be exposed in multithread environments.

  3. 2.2 Threads • Process: address space + code execution • There is no law that states that a process cannot have more than one “line” of execution. • Threads: single address space + many threads of execution

  4. 2.2 Threads • Process: address space + code execution • separate global variables • separate stack (and stack address space) • separate open files • separate signals • separate child processes, …

  5. 2.2 Threads • Process: address space + code execution • separate global variables • separate stack (and stack address space) • separate open files • separate signals • separate child processes, … • Threads: single address space + many threads of execution • shared global variables • shared stack address space (but separate stacks) • shared open files • shared signals • shared child processes, ,,,

  6. Threads • Process – used to group resources together; has at least one thread • Thread – actual entity scheduled for execution on CPU • Threads are sometimes called lightweight processes (LWPs) • Multithreading – multiple threads in the same process

  7. 2 3

  8. Thread functions • Create threads: #include <pthread.h> intpthread_create ( pthread_t* thread, pthread_attr_t* attr, void* (*start_routine)(void*), void* arg );

  9. Thread functions • Create threads: #include <pthread.h> intpthread_create ( pthread_t* thread, pthread_attr_t* attr, void* (*start_routine)(void*), void* arg ); This parameter, start_routine, is the address of a function (*start_routine). This function has one argument, a “generic” pointer (void*). This function returns a “generic” pointer (void*). (We will see an example shortly.)

  10. Thread termination void pthread_exit ( void* retval ); int pthread_cancel ( pthread_t thread );

  11. Types of processes (or threads) compute bound I/O bounds mixed

  12. Why threads? • easier to create a thread than a process • share resources • computation and I/O within a single process on a single processor can be overlapped • computation (and computation in other threads) within a single process on a multiprocessor can be overlapped

  13. Example: word processor • Multiple threads: • User interaction • Document reformatting • Automatic saving/backups • Alternative is everything else stops when (2) or (3) occurs.

  14. Example: web server • dispatcher thread – handles incoming requests • worker threads – performs request • Checks cache • Reads from disk if necessary • Adds to cache

  15. Threads and alternatives • Threads retain the simple sequential, blocking model while allowing for parallelism. • May be in user space or kernel. • (alternative) The single threaded server retains the simple sequential, block model but performance suffers (no parallelism). • (alternative) Finite state machine = each computation has a saved state and there exists some set of events that can occur to change the state. • high performance through parallelism • uses nonblocking calls and interrupts (not simple)

  16. Threads Thread types: • detached • relatively independent • joinable • waiting for completion • popup threads • like signal handlers that have their own threads

  17. Threads Implementations: • user space • scheduling is the responsibility of the user • kernel • threads are scheduled by the OS (like processes) • hybrid • win32 supports both threads (kernel), and fibers (user space)

  18. Pitfall: global variables #include <stdio.h> bool err = false; void* t1 ( void* p ) { … err = true; if (err) puts( "hello" ); //may never get here! … } void* t2 ( void* p ) { … err = false; … } int main ( intargc, char* argv[] ) { … if (err) puts( "something bad happened." ); … } global variable (shared by all threads)

  19. Pitfall: global variables #include <stdio.h> bool err = false; void* t1 ( void* p ) { … ::err = true; if (::err) puts( "hello" ); //may never get here! … } void* t2 ( void* p ) { … ::err = false; … } int main ( intargc, char* argv[] ) { … if (::err) puts( "something bad happened." ); … } Interesting note: C++ scope resolution operator may be used to indicate global variable.

  20. Pitfall: global variables #include <stdio.h> bool err = false; void* t1 ( void* p ) { … err = true; if (err) puts( "hello" ); //may never get here! … } void* t2 ( void* p ) { … err = false; … } int main ( intargc, char* argv[] ) { … if (err) puts( "something bad happened." ); … } The main thread creates two additional threads, t1 and t2. Pointers indicate next line to be executed by thread.

  21. Pitfall: global variables #include <stdio.h> bool err = false; void* t1 ( void* p ) { … err = true; if (err) puts( "hello" ); //may never get here! … } void* t2 ( void* p ) { … err = false; … } int main ( intargc, char* argv[] ) { … if (err) puts( "something bad happened." ); … } Say t1 executes this line.

  22. Pitfall: global variables #include <stdio.h> bool err = false; void* t1 ( void* p ) { … err = true; if (err) puts( "hello" ); //may never get here! … } void* t2 ( void* p ) { … err = false; … } int main ( intargc, char* argv[] ) { … if (err) puts( "something bad happened." ); … } But it gets interrupted. Now it’s t2’s turn.

  23. Pitfall: global variables #include <stdio.h> bool err = false; void* t1 ( void* p ) { … err = true; if (err) puts( "hello" ); //may never get here! … } void* t2 ( void* p ) { … err = false; … } int main ( intargc, char* argv[] ) { … if (err) puts( "something bad happened." ); … } So t2 executes. Now it’s t1’s turn.

  24. Pitfall: global variables #include <stdio.h> bool err = false; void* t1 ( void* p ) { … err = true; if (err) puts( "hello" ); //may never get here! … } void* t2 ( void* p ) { … err = false; … } int main ( intargc, char* argv[] ) { … if (err) puts( "something bad happened." ); … } t1 doesn’t print “hello.” Why not? It set err to true on the previous line! Note: We get a different result if t2 went first!

  25. Pitfalls: global variables

  26. Pitfall: global variables Possible solutions: • Don’t use ‘em. • Create thread-wide globals (using your own libraries). • Other mechanisms (that we will explore in the future).

  27. Other pitfalls • Libraries may not be reentrant • Solution: rewrite the library • Solution: wrap each library call with a wrapper • Signals, alarms, and I/O may not be thread specific.

  28. Other pthread functions • int thread_yield ( ); • int pthread_join ( pthread_t th, void** thread_return ); • Many, many others

  29. Multithreaded example

  30. /* file: pt1.cpp date: 23-sep-2005 author: george j. grevera, ph.d. compile: g++ -o pt1.exe pt1.cpp -lpthread -lm desc.: shell of a multithreaded app */ #include <math.h> #include <pthread.h> #include <stdio.h> //global variables const int N = 10; //max number of threads pthread_t thread[ N ]; //for thread id storage //---------------------------------------------------------------------- Additional system libraries that are needed.

  31. //----------------------------------------------------------------------//---------------------------------------------------------------------- //program execution begins here. int main ( intargc, char* argv[] ) { //create N threads for (inti=0; i<N; i++) { pthread_create( &thread[i], 0, start_routine, (void*)i ); printf( "main: thread %d created. \n", thread[i] ); } //wait for the N threads to finish for (inti=0; i<N; i++) { void* v; printf( "main: waiting \n" ); pthread_join( thread[i], &v ); } printf( "main: returning \n" ); return 0; } //----------------------------------------------------------------------

  32. //----------------------------------------------------------------------//---------------------------------------------------------------------- //example worker thread function. //this function does a lot of “work” (i.e., computation). double doWork ( const intwhoAmI ) { double sum = 0.0; for (inti=0; i<10000000; i++) { sum += sin( i ) * cos( i ); } return sum; } //---------------------------------------------------------------------- //main thread function void* start_routine ( void* p ) { intwhoAmI = (int)p; printf( "%d processing \n", whoAmI ); doWork( whoAmI ); printf( "%d exit \n", whoAmI ); return 0; } //----------------------------------------------------------------------

  33. complete example /* file: pt1.cpp date: 23-sep-2005 author: george j. grevera, ph.d. compile: g++ -o pt1.exe pt1.cpp -lpthread -lm desc.: shell of a multithreaded app */ #include <math.h> #include <pthread.h> #include <stdio.h> //global variables const int N = 10; //max number of threads pthread_t thread[N]; //for thread id storage //---------------------------------------------------------------------- //example worker thread function. this function does a lot of "work" // (i.e., computation). double doWork ( const int whoAmI ) { double sum = 0.0; for (int i=0; i<10000000; i++) { sum += sin( i ) * cos( i ); } return sum; } //---------------------------------------------------------------------- //main thread function void* start_routine ( void* p ) { int whoAmI = (int)p; printf( "%d processing \n", whoAmI ); doWork( whoAmI ); printf( "%d exit \n", whoAmI ); return 0; } //---------------------------------------------------------------------- //program execution begins here. int main ( const int argc, const char* const argv[] ) { //create N threads for (int i=0; i<N; i++) { pthread_create( &thread[i], 0, start_routine, (void*)i ); printf( "main: thread %d created. \n", thread[i] ); } //wait for the N threads to finish for (int i=0; i<N; i++) { void* v; printf( "main: waiting \n" ); pthread_join( thread[i], &v ); } printf( "main: returning \n" ); return 0; } //----------------------------------------------------------------------

  34. Multithreading discussion • pthread_create only allows a single parameter to be passed to the thread • pthread_join only allows a single parameter to be returned from a thread • How can we pass and return many parameters?

  35. Multithreading: advanced topiC

  36. Multithreading: advanced topic • We know that process memory is shared among all threads. • We know that the stack is part of the process memory. • Therefore the stack is part of the memory that is shared among the threads. • How can we demonstrate that the stack is shared among threads?

  37. /* This program demonstrates that, although stack variables are not shared among threads, stack memory (_all_ process memory) is indeed shared by threads. g++ -o sharedStack.exe sharedStack.cpp -lpthread -lm -lrt */ #include <iostream> #include <math.h> #include <pthread.h> #include <sched.h> #include <unistd.h> using namespace std; const int N = 2; //max number of threads //this will be a pointer to a local variable in thread 0. static int* whoAmIPointer = NULL; //---------------------------------------------------------------------- From where are local variables allocated?

  38. //----------------------------------------------------------------------//---------------------------------------------------------------------- int main ( const int argc, const char* const argv[] ) { pthread_t thread[::N]; //for thread id storage //create N threads for (int i=0; i< ::N; i++) { pthread_create( &thread[i], 0, start_routine, (void*)i ); cout << "main: thread " << i << " created with id=" << thread[i] << endl; } //wait for the N threads to finish for (int i=0; i< ::N; i++) { void* v; cout << "main: wait" << endl; pthread_join( thread[i], &v ); } cout << "main: returning" << endl; return 0; } //---------------------------------------------------------------------- Nothing new here.

  39. What does :: mean in C++? //---------------------------------------------------------------------- void* start_routine ( void* p ) { int whoAmI = (int)p; int whoAmICopy = whoAmI; cout << whoAmI << " processing" << endl; if (whoAmI==0) { //is this thread 0? //make the global var point to my local var ::whoAmIPointer = &whoAmI; sched_yield(); sleep( 5 ); } else { //this is not thread 0 so wait until thread 0 sets the global var // that points to thread 0's local var. while (::whoAmIPointer==NULL) { sched_yield(); } //change thread 0's local var *::whoAmIPointer = 92; } if (whoAmI!=whoAmICopy) { cout << "Hey! Wait a minute! Somebody changed who I am from " << whoAmICopy << " to " << whoAmI << "!" << endl; } cout << whoAmI << " done" << endl << whoAmI << " exit" << endl; return 0; } //----------------------------------------------------------------------

  40. What does :: mean in C++? :: is the C++ scope resolution operator. In this case, it refers to a global variable. //---------------------------------------------------------------------- void* start_routine ( void* p ) { int whoAmI = (int)p; int whoAmICopy = whoAmI; cout << whoAmI << " processing" << endl; if (whoAmI==0) { //is this thread 0? //make the global var point to my local var ::whoAmIPointer = &whoAmI; sched_yield(); sleep( 5 ); } else { //this is not thread 0 so wait until thread 0 sets the global var // that points to thread 0's local var. while (::whoAmIPointer==NULL) { sched_yield(); } //change thread 0's local var *::whoAmIPointer = 92; } if (whoAmI!=whoAmICopy) { cout << "Hey! Wait a minute! Somebody changed who I am from " << whoAmICopy << " to " << whoAmI << "!" << endl; } cout << whoAmI << " done" << endl << whoAmI << " exit" << endl; return 0; } //----------------------------------------------------------------------

  41. //----------------------------------------------------------------------//---------------------------------------------------------------------- void* start_routine ( void* p ) { int whoAmI = (int)p; int whoAmICopy = whoAmI; cout << whoAmI << " processing" << endl; if (whoAmI==0) { //is this thread 0? //make the global var point to my local var ::whoAmIPointer = &whoAmI; sched_yield(); sleep( 5 ); } else { //this is not thread 0 so wait until thread 0 sets the global var // that points to thread 0's local var. while (::whoAmIPointer==NULL) { sched_yield(); } //change thread 0's local var *::whoAmIPointer = 92; } if (whoAmI!=whoAmICopy) { cout << "Hey! Wait a minute! Somebody changed who I am from " << whoAmICopy << " to " << whoAmI << "!" << endl; } cout << whoAmI << " done" << endl << whoAmI << " exit" << endl; return 0; } //----------------------------------------------------------------------

  42. //----------------------------------------------------------------------//---------------------------------------------------------------------- void* start_routine ( void* p ) { int whoAmI = (int)p; int whoAmICopy = whoAmI; cout << whoAmI << " processing" << endl; if (whoAmI==0) { //is this thread 0? //make the global var point to my local var ::whoAmIPointer = &whoAmI; sched_yield(); sleep( 5 ); } else { //this is not thread 0 so wait until thread 0 sets the global var // that points to thread 0's local var. while (::whoAmIPointer==NULL) { sched_yield(); } //change thread 0's local var *::whoAmIPointer = 92; } if (whoAmI!=whoAmICopy) { cout << "Hey! Wait a minute! Somebody changed who I am from " << whoAmICopy << " to " << whoAmI << "!" << endl; } cout << whoAmI << " done" << endl << whoAmI << " exit" << endl; return 0; } //----------------------------------------------------------------------

  43. //----------------------------------------------------------------------//---------------------------------------------------------------------- void* start_routine ( void* p ) { int whoAmI = (int)p; int whoAmICopy = whoAmI; cout << whoAmI << " processing" << endl; if (whoAmI==0) { //is this thread 0? //make the global var point to my local var ::whoAmIPointer = &whoAmI; sched_yield(); sleep( 5 ); } else { //this is not thread 0 so wait until thread 0 sets the global var // that points to thread 0's local var. while (::whoAmIPointer==NULL) { sched_yield(); } //change thread 0's local var *::whoAmIPointer = 92; } if (whoAmI!=whoAmICopy) { cout << "Hey! Wait a minute! Somebody changed who I am from " << whoAmICopy << " to " << whoAmI << "!" << endl; } cout << whoAmI << " done" << endl << whoAmI << " exit" << endl; return 0; } //----------------------------------------------------------------------

  44. //----------------------------------------------------------------------//---------------------------------------------------------------------- void* start_routine ( void* p ) { int whoAmI = (int)p; int whoAmICopy = whoAmI; cout << whoAmI << " processing" << endl; if (whoAmI==0) { //is this thread 0? //make the global var point to my local var ::whoAmIPointer = &whoAmI; sched_yield(); sleep( 5 ); } else { //this is not thread 0 so wait until thread 0 sets the global var // that points to thread 0's local var. while (::whoAmIPointer==NULL) { sched_yield(); } //change thread 0's local var *::whoAmIPointer = 92; } if (whoAmI!=whoAmICopy) { cout << "Hey! Wait a minute! Somebody changed who I am from " << whoAmICopy << " to " << whoAmI << "!" << endl; } cout << whoAmI << " done" << endl << whoAmI << " exit" << endl; return 0; } //----------------------------------------------------------------------

  45. //----------------------------------------------------------------------//---------------------------------------------------------------------- void* start_routine ( void* p ) { int whoAmI = (int)p; int whoAmICopy = whoAmI; cout << whoAmI << " processing" << endl; if (whoAmI==0) { //is this thread 0? //make the global var point to my local var ::whoAmIPointer = &whoAmI; sched_yield(); sleep( 5 ); } else { //this is not thread 0 so wait until thread 0 sets the global var // that points to thread 0's local var. while (::whoAmIPointer==NULL) { sched_yield(); } //change thread 0's local var *::whoAmIPointer = 92; } if (whoAmI!=whoAmICopy) { cout << "Hey! Wait a minute! Somebody changed who I am from " << whoAmICopy << " to " << whoAmI << "!" << endl; } cout << whoAmI << " done" << endl << whoAmI << " exit" << endl; return 0; } //---------------------------------------------------------------------- How could this ever be true (if threads didn’t share stack memory)?

  46. complete example /* This program demonstrates that, although stack variables are not shared among threads, stack memory (_all_ process memory) is indeed shared by threads. g++ -o sharedStack.exe sharedStack.cpp -lpthread -lm -lrt */ #include <iostream> #include <math.h> #include <pthread.h> #include <sched.h> #include <unistd.h> using namespace std; const int N = 2; //max number of threads //this will be a pointer to a local variable in thread 0. static int* whoAmIPointer = NULL; //---------------------------------------------------------------------- void* start_routine ( void* p ) { int whoAmI = (int)p; int whoAmICopy = whoAmI; cout << whoAmI << " processing" << endl; if (whoAmI==0) { //is this thread 0? //make the global var point to my local var ::whoAmIPointer = &whoAmI; sched_yield(); sleep( 5 ); } else { //this is not thread 0 so wait until thread 0 sets the global var // that points to thread 0's local var. while (::whoAmIPointer==NULL) { sched_yield(); } //change thread 0's local var *::whoAmIPointer = 92; } if (whoAmI!=whoAmICopy) { cout << "Hey! Wait a minute! Somebody changed who I am from " << whoAmICopy << " to " << whoAmI << "!" << endl; } cout << whoAmI << " done" << endl << whoAmI << " exit" << endl; return 0; } //---------------------------------------------------------------------- int main ( const int argc, const char* const argv[] ) { pthread_t thread[::N]; //for thread id storage //create N threads for (int i=0; i< ::N; i++) { pthread_create( &thread[i], 0, start_routine, (void*)i ); cout << "main: thread " << i << " created with id=" << thread[i] << endl; } //wait for the N threads to finish for (int i=0; i< ::N; i++) { void* v; cout << "main: wait" << endl; pthread_join( thread[i], &v ); } cout << "main: returning" << endl; return 0; } //----------------------------------------------------------------------

  47. Win32, threads, & fibers

  48. Win32 thread functions • CreateThread • Creates a thread to execute within the virtual address space of the calling process. • ExitThread • Ends the calling thread. • TerminateThread • Terminates a thread. • WaitForSingleObject • Waits until the specified object is in the signaled state or the time-out interval elapses. • GetExitCodeThread • Retrieves the termination status of the specified thread.

More Related