Title: Programming with Threads Part 2
1Programming with ThreadsPart 2
2Recall Conditional Variable
- pthread_mutex_t m_varPTHREAD_MUTEX_INITIALIZER
- pthread_cond_t c_varPTHREAD_COND_INITIALIZER
- pthread_mutex_lock (m_var)
- while (ltsome blocking condition is truegt)
- pthread_cond_wait (c_var, m_var)
- ltaccess shared data structruregt
- pthread_mutex_unlock(m_var)
- Note Use while not if.
3Readers/Writers Problem(example of monitor-style
programming)
- Control access to shared data
- Threads (operations) may either
- Only read data (reader)
- Write (and possibly read) data (writer)
- Multiple readers can access shared data
concurrently - At most one writer can be accessing shared data
at any time
4Schematic View of a Monitor
5A Solution
- P_mutex m
- Int Readers 0 Boolean Writer
- P_condition read_phase, write_phase
- / Code for reader /
- P_lock(m)
- While (Writer)
- P_wait (read_phase, m)
- Readers
- P_unlock(m)
- read data
- P_lock(m)
- Readers--
- If (Readers 0)
- P_signal(write_phase)
- P_unlock (m)
/ Code for writer / P_lock(m) While (readers
! 0 Writer ) P_wait (write_phase,
m) Writer true P_unlock(m) write
data P_lock(m) Writer false P_broadcast
(read_phase) P_signal(write_phase) P_unlock
(m)
Note A problem with this solution is extra wake
ups, especially on multiprocessors
6Deadlocks
- Examples
- A locks M1, B locks M2, A blocks on M2, B blocks
on M1 - Can occur on mutex locks and conditional
variables - Deadlocks are easy to detect!
- Solutions
- Lock granularity
- Order locks and acquire them in order (e.g., all
threads must acquire M1, then M2, etc.) - Two-phase locking acquire all locks youll need
first, release all locks if you fail to acquire
any one - pthread_mutex_trylock(M) returns even if lock
fails
7Priority Inversion
- Locking can subvert thread priorities
- Example
- Low priority thread A locks M
- Medium priority thread B preempts A, runs for a
long time - High priority thread C waits on M
- C cant run, even though it is the highest
priority thread! - Solution
- Priority inheritance once C locks on M, A (which
holds M) gets Cs priority (or higher) allowing A
to exit its critical section - pthread_mutex_setprioceiling( M, prio, old)
- Set prio to priority of highest-priority thread
that can lock M - Use old to store previous priority
8Threads Programming Golden Rules
- Shared data should always be accessed through a
single mutex - Define a boolean expression for the blocking
condition (expressed via program variables) for
each condition variable - Every time the value of the boolean expression
might have changed, call Broadcast for the
condition variable use Signal only if you are
absolutely certain that only one waiting thread
can enter the critical section - Broadcast (rather than signal) simplifies logic
assuming your code is designed to deal with
spurious interrupts (which it must!) - Some performance penalty
- Must be used when multiple threads should be
allowed to progress due to change in state (e.g.,
multiple readers) - Globally order locks, and acquire them in the
pre-specified order in all threads to avoid
deadlock
9Thread Design Patterns
- Common ways to structure programs using threads
- Manager/Worker
- One thread does the main work
- Create other threads to help on some parts
- Example parallel program library
- Client/Server (thread per request)
- One thread (server) listens for requests
- Create new thread to process each request
- Client/Server (thread per client)
- One thread (server) listens for new clients
- New thread created when a client attaches
- Thread is dedicated to only work for that client
10Client / Server Model(thread per request)
- / Server /
- While (1)
- get a request
- switch (request.type)
- case X P_create (taskX)
- case Y P_create (taskY)
-
-
- / Client /
- taskX() do work synchronize as needed
- Advantage simplicity
- Disadvantages
- No bound on number of clients if burst of
requests arrives results in contention for
resources (CPU, locks, memory) - Recoding to limit number of threads somewhat ugly
- Poor design unless low thread count guaranteed!
11Thread Design Patterns(continued)
- Producer/Consumer (work queue or work pile)
- Some threads (producers/clients) place requests
for work in queue - Other threads (consumers/workers) remove requests
from queue, process them - Trivial to control the number of consumers/worker
threads - Dogpile
- Similar to workpile, except all threads look for
work directly rather than using a queue - Pipeline
- Organize threads into a linear list (pipeline)
- Each thread does a portion of a task, then passes
work on to next thread in pipeline - The work to be done needs to be structured as a
linear set of tasks!
12Libraries and Compilation
- Caution Some functions in libraries may not be
thread-safe. - E.g., MPI implementations are typically not
thread-safe - Some machines (e.g., IBM) provide thread-safe and
(faster) non-thread safe libraries - Always check users guide or man page
- When using gcc, use -pthreads option for both
compilation and linking
13Summary
- Follow the golden rules and you should be OK
- Keep it simple!
- Worry about performance, but not too much
- Be cautious about compiler and libraries
14Additional References
- Books
- D. Butenhof, Programming With POSIX Threads,
Addison Wesley, 1998 - Bill Lewis and Daniel J. Berg, PThread Primer,
1996 (available online) - Bill Lewis and Daniel J. Berg, Multithreaded
Programming with PThreads. Prentice Hall, 1998 - Brief tutorial on the web
- Tutorial at LLNL (with links to man pages)
- Tutorial by Mark Hays (with long examples)
- Tutorial by Alfred Park (shorter)