MultiCore Processing Workshop Multithreaded Programming using POSIX Threads(Pthreads) Syed Akbar Mehdi.

Slides:



Advertisements
Similar presentations
CS Lecture 4 Programming with Posix Threads and Java Threads George Mason University Fall 2009.
Advertisements

Threads. Readings r Silberschatz et al : Chapter 4.
Threads. What do we have so far The basic unit of CPU utilization is a process. To run a program (a sequence of code), create a process. Processes are.
Pthreads & Concurrency. Acknowledgements  The material in this tutorial is based in part on: POSIX Threads Programming, by Blaise Barney.
Professor: Shu-Ching Chen TA: Hsin-Yu Ha.  An independent stream of instructions that can be scheduled to run  A path of execution int a, b; int c;
Multi-core Programming Programming with Posix Threads.
PTHREADS These notes are from LLNL Pthreads Tutorial
MultiCore Processing Workshop Multithreaded Programming using POSIX Threads(Pthreads) Syed Akbar Mehdi.
Computer Architecture II 1 Computer architecture II Programming: POSIX Threads OpenMP.
8-1 JMH Associates © 2004, All rights reserved Windows Application Development Chapter 10 - Supplement Introduction to Pthreads for Application Portability.
Lecture 18 Threaded Programming CPE 401 / 601 Computer Network Systems slides are modified from Dave Hollinger.
SMP threads an Introduction to Posix Threads. Technical Definition 1.Independent stream of instructions that can be scheduled to run by an operating system.
Threads© Dr. Ayman Abdel-Hamid, CS4254 Spring CS4254 Computer Network Architecture and Programming Dr. Ayman A. Abdel-Hamid Computer Science Department.
Introduction to Pthreads. Pthreads Pthreads is a POSIX standard for describing a thread model, it specifies the API and the semantics of the calls. Model.
Object Oriented Analysis & Design SDL Threads. Contents 2  Processes  Thread Concepts  Creating threads  Critical sections  Synchronizing threads.
CGS 3763 Operating Systems Concepts Spring 2013 Dan C. Marinescu Office: HEC 304 Office hours: M-Wd 11: :30 AM.
1 Threads Chapter 11 from the book: Inter-process Communications in Linux: The Nooks & Crannies by John Shapley Gray Publisher: Prentice Hall Pub Date:
Today’s topic Pthread Some materials and figures are obtained from the POSIX threads Programming tutorial at
Operating Systems CMPSC 473 Multi-threading models Tutorial on pthreads Lecture 10: September Instructor: Bhuvan Urgaonkar.
June-Hyun, Moon Computer Communications LAB., Kwangwoon University Chapter 26 - Threads.
Multi-threaded Programming with POSIX Threads CSE331 Operating Systems Design.
Threads and Thread Control Thread Concepts Pthread Creation and Termination Pthread synchronization Threads and Signals.
CS345 Operating Systems Threads Assignment 3. Process vs. Thread process: an address space with 1 or more threads executing within that address space,
POSIX Threads Programming Operating Systems. Processes and Threads In shared memory multiprocessor architectures, such as SMPs, threads can be used to.
Programming with POSIX* Threads Intel Software College.
1 Threads Chapter 11 from the book: Inter-process Communications in Linux: The Nooks & Crannies by John Shapley Gray Publisher: Prentice Hall Pub Date:
CS333 Intro to Operating Systems Jonathan Walpole.
Professor: Shu-Ching Chen TA: Samira Pouyanfar.  An independent stream of instructions that can be scheduled to run  A path of execution int a, b; int.
Pthreads: A shared memory programming model
Threads CSCE Thread Motivation Processes are expensive to create. Context switch between processes is expensive Communication between processes.
S -1 Posix Threads. S -2 Thread Concepts Threads are "lightweight processes" –10 to 100 times faster than fork() Threads share: –process instructions,
1 Pthread Programming CIS450 Winter 2003 Professor Jinhua Guo.
POSIX Synchronization Introduction to Operating Systems: Discussion Module 5.
POSIX Threads HUJI Spring 2011.
Lecture 7: POSIX Threads - Pthreads. Parallel Programming Models Parallel Programming Models: Data parallelism / Task parallelism Explicit parallelism.
Pthreads.
12/22/ Thread Model for Realizing Concurrency B. Ramamurthy.
Chapter 6 P-Threads. Names The naming convention for a method/function/operation is: – pthread_thing_operation(..) – Where thing is the object used (such.
Unix Internals Concurrent Programming. Unix Processes Processes contain information about program resources and program execution state, including: Process.
Copyright ©: Nahrstedt, Angrave, Abdelzaher
PThread Synchronization. Thread Mechanisms Birrell identifies four mechanisms commonly used in threading systems –Thread creation –Mutual exclusion (mutex)
NCHU System & Network Lab Lab #6 Thread Management Operating System Lab.
Thread Basic Thread operations include thread creation, termination, synchronization, data management Threads in the same process share:  Process address.
1 Introduction to Threads Race Conditions. 2 Process Address Space Revisited Code Data OS Stack (a)Process with Single Thread (b) Process with Two Threads.
7/9/ Realizing Concurrency using Posix Threads (pthreads) B. Ramamurthy.
Tutorial 4. In this tutorial session we’ll see Threads.
A thread is a basic unit of CPU utilization within a process Each thread has its own – thread ID – program counter – register set – stack It shares the.
CS 537 – Introduction to Operating Systems
Principles of Operating Systems Lecture 11
Shared-Memory Programming with Threads
Threads Threads.
CS399 New Beginnings Jonathan Walpole.
Thread Programming.
PTHREADS These notes are from LLNL Pthreads Tutorial
Multithreading Tutorial
Principles of Operating Systems Lecture 8
PTHREADS AND SEMAPHORES
Multithreading Tutorial
Thread Programming.
CS510 Operating System Foundations
Jonathan Walpole Computer Science Portland State University
Pthread Prof. Ikjun Yeom TA – Mugyo
Multithreading Tutorial
Programming with Shared Memory
Multithreading Tutorial
Programming with Shared Memory
Tutorial 4.
Programming with Shared Memory - 2 Issues with sharing data
Shared Memory Programming with Pthreads
POSIX Threads(pthreads)
Presentation transcript:

MultiCore Processing Workshop Multithreaded Programming using POSIX Threads(Pthreads) Syed Akbar Mehdi

Outline 1.Preliminaries and Introduction 2.Thread Management 3.Synchronization 4.Exercises.

Part 1. Preliminaries OS Basics Virtual Address Space Program Execution Basics Processes vs Threads POSIX Threads

Computer System Organization  Computer-system operation  One or more CPUs, device controllers connect through common bus providing access to shared memory  Concurrent execution of CPUs and devices competing for memory cycles

What is an OS?  software between applications and reality:  abstracts hardware and makes useful and portable  makes finite into (near)infinite  provides protection Visual Studio MS Word Half-Life 2 OS hardware

What is a Process?  A process is an “instance” of a program running.  Modern OSes run multiple processes simultaneously  Examples (can all run simultaneously):  gcc file_A.c – compiler running on file A  gcc file_B.c – compiler running on file B  emacs – text editor  firefox – web browser  Non-examples (implemented as one process):  Multiple firefox tabs are part of one process.  Why processes?  Simplicity of programming  Higher throughput (better CPU utilization), lower latency

What is a Process?  Each proc. P i has own view of machine  Its own address space.  Its own open files.  Its own virtual CPU (through preemptive multitasking)  *(char *)0xc000 different in P 1 & P 2  Greatly simplifies programming model  gcc does not care that firefox is running  Sometimes want interaction between processes  Simplest is through files: emacs edits file, gcc compiles it  More complicated: Shell/command, Window manager/app.

More about Processes

Process Switching

Process Organization in Memory

Basic Execution

Basic Execution Environment int main( ) { } int foo1 (int) {} int foo2 (int) {} gvar = 100 var1 = 2 var2 = 3 Stack Global Text IPFP SP main() int gvar = 100; int foo2 (int b) { return b * gvar; } int foo1 (int a) { int lvar = a + gvar; return foo2(lvar); } int main ( ) { int var1, int var2; var1 = 2; var2 = 3; var1 = foo1(var1); var2 = foo1(var2); return 0; } Heap

Basic Execution Environment int gvar = 100; int foo2 (int b) { return b * gvar; } int foo1 (int a) { int lvar = a + gvar; return foo2(lvar); } int main ( ) { int var1, int var2; var1 = 2; var2 = 3; var1 = foo1(var1); var2 = foo1(var2); return 0; } int main( ) { } int foo1 (int) {} int foo2 (int) {} gvar = 100 var1 = 2 var2 = 3 Stack Global Text IPFP SP main() a = 2 lvar = 102 foo1() Heap

Basic Execution Environment int main( ) { } int foo1 (int) {} int foo2 (int) {} gvar = 100 var1 = 2 var2 = 3 Stack Global Text IPFP SP main() a = 2 lvar = 102 foo1() foo2() b = 102 int gvar = 100; int foo2 (int b) { return b * gvar; } int foo1 (int a) { int lvar = a + gvar; return foo2(lvar); } int main ( ) { int var1, int var2; var1 = 2; var2 = 3; var1 = foo1(var1); var2 = foo1(var2); return 0; } Heap

Basic Execution Environment int gvar = 100; int foo2 (int b) { return b * gvar; } int foo1 (int a) { int lvar = a + gvar; return foo2(lvar); } int main ( ) { int var1, int var2; var1 = 2; var2 = 3; var1 = foo1(var1); var2 = foo1(var2); return 0; } int main( ) { } int foo1 (int) {} int foo2 (int) {} gvar = 100 var1 = 2 var2 = 3 Stack Global Text IPFP SP main() a = 2 lvar = 102 foo1() Heap

Basic Execution Environment int main( ) { } int foo1 (int) {} int foo2 (int) {} gvar = 100 var1 = var2 = 3 Stack Global Text IPFP SP main() int gvar = 100; int foo2 (int b) { return b * gvar; } int foo1 (int a) { int lvar = a + gvar; return foo2(lvar); } int main ( ) { int var1, int var2; var1 = 2; var2 = 3; var1 = foo1(var1); var2 = foo1(var2); return 0; } Heap

Basic Execution Environment int gvar = 100; int foo2 (int b) { return b * gvar; } int foo1 (int a) { int lvar = a + gvar; return foo2(lvar); } int main ( ) { int var1, int var2; var1 = 2; var2 = 3; var1 = foo1(var1); var2 = foo1(var2); return 0; } int main( ) { } int foo1 (int) {} int foo2 (int) {} gvar = 100 var1 = var2 = 3 Stack Global Text IPFP SP main() a = 3 lvar = 103 foo1() Heap

Basic Execution Environment int main( ) { } int foo1 (int) {} int foo2 (int) {} gvar = 100 var1 = var2 = 3 Stack Global Text IPFP SP main() a = 3 lvar = 103 foo1() foo2() b = 103 int gvar = 100; int foo2 (int b) { return b * gvar; } int foo1 (int a) { int lvar = a + gvar; return foo2(lvar); } int main ( ) { int var1, int var2; var1 = 2; var2 = 3; var1 = foo1(var1); var2 = foo1(var2); return 0; } Heap

Basic Execution Environment int gvar = 100; int foo2 (int b) { return b * gvar; } int foo1 (int a) { int lvar = a + gvar; return foo2(lvar); } int main ( ) { int var1, int var2; var1 = 2; var2 = 3; var1 = foo1(var1); var2 = foo1(var2); return 0; } int main( ) { } int foo1 (int) {} int foo2 (int) {} gvar = 100 var1 = var2 = 3 Stack Global Text IPFP SP main() a = 3 lvar = 103 foo1() Heap

Basic Execution Environment int main( ) { } int foo1 (int) {} int foo2 (int) {} gvar = 100 var1 = var2 = Stack Global Text IPFP SP main() int gvar = 100; int foo2 (int b) { return b * gvar; } int foo1 (int a) { int lvar = a + gvar; return foo2(lvar); } int main ( ) { int var1, int var2; var1 = 2; var2 = 3; var1 = foo1(var1); var2 = foo1(var2); return 0; } Heap

What is a thread?  What’s needed to run code on CPU  “execution stream in an execution context”  Execution stream: sequential seq. of instructions  CPU execution context (1 thread)  State: stack, heap, registers  Position: Instruction Pointer(IP) register  OS execution context (n threads):  identity + open file descriptors, page table, …

What is a thread?

 All threads in a process share the same address space.  *(char *)0xc000 means “the same” in thread T1 and T2.  All threads share the same file descriptors.  Which implies that they share network sockets.  All threads have access to the same heap and same global variables.  Write access to global variables should be protected by a synchronization mechanism.  Each thread has its separate stack, Instruction Pointer and Local variables.  Therefore each thread has its own independent flow of execution

What is a thread?

Pthreads  Historically, hardware vendors have implemented their own proprietary versions of threads.  These implementations differed significantly from each other resulting in reduced portability.  In order to take full advantage of the capabilities provided by threads, a standardized programming interface was required.  For UNIX systems, this interface has been specified by the IEEE POSIX c standard (1995).  Implementations adhering to this standard are referred to as POSIX threads, or Pthreads.  Most hardware vendors now offer Pthreads in addition to their proprietary API's.  Pthreads are defined as a set of C language programming types and procedure calls, implemented with a pthread.h header/include file and a thread library.

Pthreads The subroutines which comprise the Pthreads API can be informally grouped into four major groups:  Thread management: Routines that work directly on threads  Mutexes: Routines that deal with synchronization, called a "mutex", which is an abbreviation for "mutual exclusion"  Condition variables: Routines that address communications between threads that share a mutex.  Synchronization: Routines that manage read/write locks and barriers.

Pthreads Routine PrefixFunctional Group pthread_ Threads themselves and miscellaneous subroutines pthread_attr_Thread attributes objects pthread_mutex_Mutexes pthread_mutexattr_Mutex attributes objects. pthread_cond_Condition variables pthread_condattr_Condition attributes objects pthread_key_Thread-specific data keys pthread_rwlock_Read/write locks pthread_barrier_Synchronization barriers

Part 2. Thread Management Creating and Terminating Threads Passing Arguments to Threads Joining and Detaching Threads Setting Thread Attributes Miscellaneous Routines

Creating and Terminating Threads The following functions are used for creating and terminating threads: 1.pthread_create (thread,attr,start_routine,arg) 2.pthread_exit (status) 3.pthread_attr_init (attr) 4.pthread_attr_destroy (attr)

Creating Threads  Initially, your main() program comprises a single, default thread.  All other threads must be explicitly created by the programmer.  The maximum number of threads that may be created by a process is implementation dependent.  Once created, threads are peers, and may create other threads.  There is no implied hierarchy or dependency between threads.

Creating Threads int pthread_create(pthread_t *thr, const pthread_attr_t *attr, void *(*start_routine)(void), void *arg) pthread_t *thr const pthread_attr_t *attr void *(*start_routine)(void) void *arg Will contain the newly created thread’s id. Must be passed by reference Give the attributes that this thread will have. Use NULL for the default ones. The name of the function that the thread will run. Must have a void pointer as its return and parameters values The argument for the function that will be the body of the Pthreads Pointers of the type void can reference ANY type of data, but they CANNOT be used in any type of operations that reads or writes its data without a cast Return a non zero value in success

Terminating Threads There are several ways in which a Pthread may be terminated.  The thread returns from its starting routine  This means the main() function for the initial thread.  The thread makes a call to the pthread_exit subroutine.  Typically, the pthread_exit() routine is called after a thread has completed its work and is no longer required to exist.  The thread is canceled by another thread via the pthread_cancel routine.  The entire process is terminated due to a call to either the exec or exit subroutines.  If main() finishes before the threads it has created.  If it uses pthread_exit(), the other threads will continue to execute.  If main simply returns they will be automatically terminated.

Misc. Useful Functions pthread_t pthread_self(void) void pthread_exit(void *arg); Return the id of the calling thread. Returns a pthread_t type which is usually an integer type variable OpenMP Counterpart int omp_get_thread_num(void); This function will indicate the end of a Pthread and the returning value will be put in arg

“Hello World” Example #include #define NUM_THREADS 4 void* work(void *i){ printf("Hello, world from %i\n", pthread_self()); pthread_exit(NULL); } int main(int argc, char **argv){ int i; pthread_t id[NUM_THREADS]; for(i = 0; i < NUM_THREADS; ++i){ if(pthread_create(&id[i], NULL, work, NULL)){ printf("Error creating the thread\n"); exit(-1); } printf("After creating the thread. My id is: %i\n", pthread_self()); return 0;} Hello, world from 2 Hello, world from 3 After creating the thread. My id is: 1 Hello, world from 4 Hello, world from 2 Hello, world from 3 Hello, world from 4 After creating the thread. My id is: 1 Hello, world from 5 What happened to thread 5???

Passing Arguments to Threads  Single Argument Passing  Cast its value as a void pointer (a tricky pass by value)  Cast its address as a void pointer (pass by reference).  The value that the address is pointing should NOT change between Pthreads creation  Multiple Argument Passing  Heterogonous: Create an structure with all the desired arguments and pass an element of that structure as a void pointer.  Homogenous: Create an array and then cast it as a void pointer

Passing a Single Argument Hello, world from 2 with value 1 Hello, world from 3 with value 2 Hello, world from 6 with value 5 Hello, world from 5 with value 5 Hello, world from 4 with value 4 Hello, world from 8 with value 9 Hello, world from 9 with value 9 Hello, world from 10 with value 9 Hello, world from 7 with value 6 Hello, world from 11 with value 10 #include #define NUM_THREADS 10 void *work(void *i){ int f = *((int *)(i)); printf("Hello, world from %i with value %i\n", pthread_self(), f); pthread_exit(NULL); } int main(int argc, char **argv){ int i; pthread_t id[NUM_THREADS]; for(i = 0; i < NUM_THREADS; ++i){ if(pthread_create(&id[i], NULL, work, (void *)(&i))){ printf("Error creating the thread\n"); exit(-1);} } return 0; } Wrong Method!!!!

Passing a Single Argument Hello, world from 2 with value 0 Hello, world from 3 with value 1 Hello, world from 4 with value 2 Hello, world from 5 with value 3 Hello, world from 6 with value 4 Hello, world from 7 with value 5 Hello, world from 8 with value 6 Hello, world from 10 with value 8 Hello, world from 11 with value 9 #include #define NUM_THREADS 10 void *work(void *i){ int f = (int)(i); printf("Hello, world from %i with value %i\n", pthread_self(), f); pthread_exit(NULL); } int main(int argc, char **argv){ int i; pthread_t id[NUM_THREADS]; for(i = 0; i < NUM_THREADS; ++i){ if(pthread_create(&id[i], NULL, work, (void *)(i))){ printf("Error creating the thread\n"); exit(-1); } return 0; } Right Method 1

Passing a Single Argument Hello, world from 2 with value 0 Hello, world from 4 with value 2 Hello, world from 5 with value 3 Hello, world from 6 with value 4 Hello, world from 7 with value 5 Hello, world from 8 with value 6 Hello, world from 9 with value 7 Hello, world from 3 with value 1 Hello, world from 10 with value 8 Hello, world from 11 with value 9 #include #define NUM_THREADS 10 void *work(void *i){ int f = *((int *)(i)); printf("Hello, world from %i with value %i\n", pthread_self(), f); pthread_exit(NULL); } int main(int argc, char **argv){ int i; int y[NUM_THREADS]; pthread_t id[NUM_THREADS]; for(i = 0; i < NUM_THREADS; ++i){ y[i] = i; if(pthread_create(&id[i], NULL, work, (void *)(&y[i]))){ printf("Error creating the thread\n"); exit(-1); } return 0; } Right Method 2

Thread Joining  Joining is a way to accomplish “coarse grained” synchronization between threads.  The pthread_join() subroutine blocks the calling thread until the thread with the specified “id” terminates.  A joining thread can match one pthread_join() call.  It is a logical error to attempt multiple joins on the same thread.

Thread Joining The Joining of All Loose Ends: pthread_join int pthread_join(pthread_t id, void **tr); pthread_t id void **tr The id of a created thread A pointer to the result of the thread Make sure that the thread that has this id returns. Otherwise waits for it OpenMP Counterpart #pragma omp barrier Why use it? If the main thread dies, then all other threads will die with it. Even if they have not completed their work Returns a non zero value in success T3 T2 T1 Main Premature thread death T3 T2 T1 Main Join point

Thread Joining Hello, world from 2 Hello, world from 3 Hello, world from 4 After creating the thread. My id is: 1 Hello, world from 5 After joining #include #define NUM_THREADS 4 void *work(void *i){ printf("Hello, world from %i\n", pthread_self()); pthread_exit(NULL); } int main(int argc, char **argv){ int i; pthread_t id[NUM_THREADS]; for(i = 0; i < NUM_THREADS; ++i){ if(pthread_create(&id[i], NULL, work, NULL)){ exit(-1); } printf("After creating the thread. My id is: %i\n“, pthread_self()); for(i = 0; i < NUM_THREADS; ++i){ if(pthread_join(id[i], NULL)){ exit(-1); } printf("After joining\n"); return 0; }

Thread Attributes  By default, a thread is created with certain attributes. Some of these attributes can be changed by the programmer via the thread attribute object.  Thread attributes help the programmer customize the behavior of thread execution.  pthread_attr_init and pthread_attr_destroy are two functions used to initialize/destroy the thread attribute object.  Other routines are then used to query/set specific attributes in the thread attribute object.

Thread Attributes int pthread_attr_init(pthread_attr_t *attr); int pthread_attr_destroy(pthread_attr_t *attr); int pthread_attr_setdetachstate(pthread_attr_t *attr, int JOIN_STATE); Initialize an attribute with the default values for the attribute object Default Schedule: SCHED_OTHER (?) Default Scope: PTHREAD_SCOPE_SYSTEM (?) Default Join State: PTHREAD_CREATE_JOINABLE (?) De-allocate any memory and state that the attribute object occupied. It is safe to delete the attribute object after the thread has been created Set the attached parameter on the attribute object with the JOIN_STATE variable PTHREAD_CREATE_JOINABLE: It can be joined at a join point. State must be saved after function ends PTHREAD_CREATE_DETACHED: It cannot be joined at a join point. State and resources are de-allocated immediately

Thread Attributes int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy) int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *pr) int pthread_attr_setinheritsched(pthread_attr_t *attr, int inherit) int pthread_attr_setscope(pthread_attr_t *attr, int scope) Set the scheduling policy of the thread: SCHED_OTHER  Regular scheduling SCHED_RR  Round-robin (SU) SCHED_FIFO  First-in First-out (SU) Contains the schedule priority of the thread Default: 0 Tell if the scheduling parameters will be inherit from the parent or the ones in the attribute object will be used PTHREAD_EXPLICIT_SCHED  Scheduling parameters from the attribute object will be used. PTHREAD_INHERIT_SCHED  inherit the attributes from its parent. Contention parameter PTHREAD_SCOPE_SYSTEM PTHREAD_SCOPE_PROCESS

Thread Attributes #include #define NUM_THREADS 4 struct args{int a; float b; char c;}; void *work(void *i){ struct args *a = (struct args *)(i); printf("(%3i, %.3f, %3c) --> %i\n", a->a, a->b, a->c, pthread_self()); pthread_exit(NULL); } int main(int argc, char **argv){ int i; struct args a[NUM_THREADS]; pthread_t id[NUM_THREADS]; pthread_attr_t ma; pthread_attr_init(&ma); pthread_attr_setdetachstate(&ma, PTHREAD_CREATE_JOINABLE); for(i = 0; i < NUM_THREADS; ++i){ a[i].a = i; a[i].b = 1.0 /(i+1); a[i].c = 'a' + (char)(i); pthread_create(&id[i], &ma, work, (void *)(&a[i])); } pthread_attr_destroy(&ma); for(i = 0; i < NUM_THREADS; ++i){pthread_join(id[i], NULL);} return 0; } ( 0, 1.000, a) --> 2 ( 3, 0.250, d) --> 5 ( 2, 0.333, c) --> 4 ( 1, 0.500, b) --> 3

Miscellaneous Useful Functions int pthread_attr_getstackaddr (const pthread_attr_t *attr, void **stackaddr) int pthread_attr_getstacksize (const pthread_attr_t *attr, size_t *stacksize) Return the stack address that this P-thread will be using Return the stack size that this P-thread will be using int pthread_detach (pthread_t thr, void **value_ptr) Make the thread that is identified by thr not joinable int pthread_once (pthread_once_t *once_control, void (*init_routine)(void)); Make sure that the init_routine is executed by a single thread and only once. The once_control is a synchronization mechanism that can be defined as: pthread_once_t once_control = PTHREAD_ONCE_INIT; OpenMP Counterpart #pragma omp single void pthread_yield () Relinquish the use of the processor

Exercises  Compile and run the example code from the slides  Implement vector addition using Pthreads.  Implement matrix multiplication using Pthreads.  Try chunking and cyclic distribution for different matrix sizes and different core counts and observe the performance.

Part 3. Thread Synchronization Mutexes Read-Write Locks Condition Variables

Synchronization Types Mutex Semaphores Monitors Conditional Variables Reader / Writer Locks Mutual Exclusion Lock. Only the thread that has the lock can access the protected region A counter of resources that are available. Zero means no resources are left. Binary (Mutex) and Counting Act as a guard of some resource. Consists of a mutex with some kind of notification scheme Synchronization occurs when a condition is met. Always used in conjunction with a mutex. Inter- thread (process) communication. Permits only reads on a data or writes on data in a group. In other words, lock out the writers when the readers are on the shared data and vice versa. pthreads Java pthreads

Mutex  Mutex is an abbreviation for "mutual exclusion".  A mutex variable acts like a "lock" protecting access to a shared data resource.  Only one thread can lock (or own) a mutex variable at any given time.  If multiple threads try to lock a mutex at the same time, the first thread gets access and others are blocked.  They must wait their turn to lock the mutex.  Used to protect access to shared data visible to all threads  This usually means global variables and data structures.

Mutex  Mutexes prevent race conditions on global data. Thread 1Thread 2Balance Read balance: $1000 $1000 Read balance: $1000 $1000 Deposit $200$1000 Deposit $200 $1000 Update balance $1000+$200 $1200 Update balance $1000+$200 $1200  Remember in an actual program, even incrementing a global variable with an operation like var++ is not “atomic”.  It gets converted into assembly like:  LOAD var, R1  ADD R1, 1  STORE R1, var

Mutex int pthread_mutex_init(pthread_mutex_t *m, const pthread_mutexattr_t *ma); int pthread_mutex_lock(pthread_mutex_t *m); int omp_lock_init(omp_lock_t *lkc); int omp_lock_set(omp_lock_t *lkc); int pthread_mutex_unlock(pthread_mutex_t *m);int omp_lock_unset(omp_lock_t *lkc); int pthread_mutex_destroy(pthread_mutex_t *m); int omp_lock_destroy(omp_lock_t *lkc); Pthread OpenMP pthread_mutexattr_t * ma can be left NULL for the default values  Initialization 2  Acquire Lock 3  Release Lock 4  Destroying

Mutex The Initial Balance: The Final Balance: #include #define NUM_THREADS 2 #define CYCLE 100 double b; void *deposit(void *i){ double m = *(double *)(i); int j; for(j = 0; j < CYCLE; ++j){ b += m; sleep(1); }} void *withdraw(void *i){double m = *(double *)(i); int j; for(j = 0; j < CYCLE; ++j){ b -= m; sleep(1); }} int main(int argc, char **argv){ int i; double bi; double q = 10.0; pthread_t id[NUM_THREADS]; b = ; bi = b; pthread_create(&id[0], NULL, deposit, (void *)(&q)); pthread_create(&id[1], NULL, withdraw, (void *)(&q)); for(i = 0; i < NUM_THREADS; ++i){pthread_join(id[i], NULL);} printf("The Initial Balance: %.2lf\nThe Final Balance: %.2lf\n", bi, b); return 0; }

Mutex The Initial Balance: The Final Balance: … pthread_mutex_t mt; void *deposit(void *i){ double m = *(double *)(i); int j; for(j = 0; j < CYCLE; ++j){ pthread_mutex_lock(&mt); b += m; pthread_mutex_unlock(&mt); sleep(1); } } void *withdraw(void *i){ double m = *(double *)(i); int j; for(j = 0; j < CYCLE; ++j){ pthread_mutex_lock(&mt); b -= m; pthread_mutex_unlock(&mt); sleep(1); } } int main(int argc, char **argv){ … pthread_mutex_init(&mt, NULL); … pthread_mutex_destroy(&mt); return 0; }

Mutex  If multiple threads try to acquire a lock which is already held by some thread, then they are blocked and enter into a queue.  So which thread acquires the lock when the owning thread unlocks it?  It depends on the implementation  Maybe highest priority thread blocked in the queue.  Or maybe in FIFO order.

Non-Blocking Mutex Lock int pthread_mutex_trylock (pthread_mutex_t *mutex);  pthread_mutex_trylock() will attempt to lock a mutex. However, if the mutex is already locked, the routine will return immediately with a EBUSY error code.  This routine may be useful in preventing deadlock conditions, as in a priority-inversion situation.

Read-Write Locks  Used in a situation when you have multiple readers and a single or multiple writers accessing shared memory.  Used to increase efficiency of access to shared data structures.  Multiple readers can read at the same time.  Only one writer at a time.

Read-Write Locks int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr); int pthread_rwlock_destroy(pthread_rwlock_t *rwlock); int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); Initialize a read-write lock rwlock with the attributes given by attr. The attr can be left NULL so it will use the default attributes. De-allocated any resources associated with the lock rwlock Acquire a read lock to the read-write lock referenced by rwlock. The calling thread acquires the read lock if a writer does not hold the lock and there are no writers blocked on the lock.

Read-Write Locks int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); Acquire a write lock to the read-write lock referenced by rwlock. The calling thread acquires the write lock if no other thread (reader or writer) holds the read-write lock rwlock. int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);  Unlock the rwlock held by the current thread.  If there are other read locks currently held on this read- write lock object, the read-write lock object remains in the read locked state.  Otherwise lock becomes available.  What happens if other threads are blocked when it becomes available?  Depends on the implementation and scheduling policy.  Usually writers take precedence in order to prevent starvation.

Condition Variables  Another way for threads to synchronize.  Difference with Mutex: Condition variables allow threads to synchronize based upon the actual value of data.  Without condition variables, the programmer would need to have threads continually polling (possibly in a critical section), to check if a condition is met.  A condition variable is always used in conjunction with a mutex lock.

Condition Variables int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *attr) int pthread_cond_destroy(pthread_cond_t *cond) int pthread_condattr_init (pthread_condattr_t *attr) int pthread_condattr_destroy (pthread_condattr_t *attr) Initialize a conditional variable cond with the attributes given by attr. The attr can be left NULL so it will use the default variable De-allocated any resources associated with the conditional variable cond Initialize the conditional attribute variable attr with the default value. De-allocate the conditional attribute variable attr. It is safe to de- allocate the variable just after the conditional variable has been initialized

Condition Variables int pthread_cond_wait(pthread_cond_t *condition, pthread_mutex_t *m) int pthread_cond_signal(pthread_cond_t *condition) int pthread_cond_broadcast (pthread_cond *condition) Tricky Conditional Variables Make the calling thread wait for a signal in the conditional variable. Must be called when the associated mutex is locked and it will unlock it while the thread blocks. It will also unlock the mutex if the signal has been received. Signal a thread that has been blocked waiting for condition to become true. It must be called after its associated mutex has been locked and the mutex must be unlocked after the signal has been issued Similar to pthread_cond_signal but it signals all the waiting threads for this conditional variable

Condition Variables bool empty = true; int value = -1; void * f1(){ while( empty) { } printf(“The value I read is %d\n”, value); } void * f2(){ sleep(10); value = rand() % 10; empty = false; printf(“The value produced was %d\n”, value); } int main () { //create two threads with f1() and f2() as starting functions …… } Without using condition variables the thread starting with function f1() is wastefully burning CPU cycles.

Condition Variables bool empty = true; int value = -1; pthread_cond_t condition_cond; pthread_mutex_t condition_mutex; void * f1(){ pthread_mutex_lock( &condition_mutex ); while( empty){ pthread_cond_wait( &condition_cond, &condition_mutex ); } printf(“The value I read is %d\n”, value); pthread_mutex_unlock( &condition_mutex ); } void * f2(){ sleep(10); pthread_mutex_lock( &condition_mutex ); value = rand() % 10; empty = false; pthread_cond_signal(&condition_cond ); pthread_mutex_unlock( &condition_mutex ); printf(“The value produced was %d\n”, value); } int main () { pthread_cond_init(&condition_cond, NULL); pthread_mutex_init(&condition_mutex, NULL); //create two threads with f1() and f2() as starting functions //Join threads // Destroy the mutex and condition variable }

References  POSIX Threads Programming:  POSIX Standard:  "Pthreads Programming". B. Nichols et al. O'Reilly and Associates.  "Threads Primer". B. Lewis and D. Berg. Prentice Hall  "Programming With POSIX Threads". D. Butenhof. Addison Wesley  "Programming With Threads". S. Kleiman et al. Prentice Hall