intro threads
TRANSCRIPT
-
7/27/2019 Intro Threads
1/29
An Introduction to
Programming with Threads
-
7/27/2019 Intro Threads
2/29
Resources
Birrell - "An Introduction to
Programming with Threads"
Silberschatz et al., 7th ed, Chapter 4
-
7/27/2019 Intro Threads
3/29
Threads
A thread is a single sequential flow ofcontrol
A process can have many threads and asingle address space
Threads share memory and, hence, need to
cooperate to produce correct results Thread has thread specific data (registers,
stack pointer, program counter)
-
7/27/2019 Intro Threads
4/29
Threads continued..
-
7/27/2019 Intro Threads
5/29
Why use threads
Threads are useful because of real-worldparallelism:
input/output devices (flesh or silicon) may beslow but are independent -> overlap IO andcomputation
distributed systems have many computing entities
multi-processors/multi-core are becoming morecommon
better resource sharing & utilization thenprocesses
-
7/27/2019 Intro Threads
6/29
Thread Mechanisms
Birrell identifies four mechanisms used inthreading systems: thread creation
mutual exclusion
waiting for events
interrupting a threads wait
In most mechanisms in current use, only thefirst three are covered
In the paper - primitives used abstract, notderived from actual threading system or
programming language!
-
7/27/2019 Intro Threads
7/29
Example Thread Primitives
Thread creation Threadtype
Fork(proc, args)returns thread
Join(thread)returns value
Mutual Exclusion Mutex type
Lock(mutex), a block-structured languageconstruct in this lecture
-
7/27/2019 Intro Threads
8/29
Example Thread Primitives
Condition Variables Conditiontype
Wait(mutex, condition)
Signal(condition)
Broadcast(condition)
Fork, Wait, Signal, etc. are not to be confusedwith the UNIX fork, wait, signal, etc.calls
-
7/27/2019 Intro Threads
9/29
Creation Example
{
Thread thread1;
thread1 = Fork(safe_insert, 4);
safe_insert(6);
Join(thread1); // Optional
}
-
7/27/2019 Intro Threads
10/29
Mutex Example
list my_list;
Mutex m;
void safe_insert(int i) {
Lock(m) {
my_list.insert(i);
}}
-
7/27/2019 Intro Threads
11/29
Condition Variables
Mutexes are used to control access to shared data only one thread can execute inside a Lock clause
other threads who try to Lock, are blocked untilthe mutex is unlocked
Condition variables are used to wait for specificevents
free memory is getting low, wake up the garbagecollector thread
10,000 clock ticks have elapsed, update thatwindow
new data arrived in the I/O port, process it
Could we do the same with mutexes?
(think about it and well get back to it)
-
7/27/2019 Intro Threads
12/29
Condition Variable Example
Mutex io_mutex;
Condition non_empty;
...
Consumer:
Lock (io_mutex) {while (port.empty())
Wait(io_mutex, non_empty);
process_data(port.first_in());
}
Producer:
Lock (io_mutex) {
port.add_data();
Signal(non_empty);
}
-
7/27/2019 Intro Threads
13/29
Condition Variables Semantics
Each condition variable is associated with asingle mutex
Wait atomically unlocks the mutex and blocks
the thread Signal awakes a blocked thread
the thread is awoken inside Wait
tries to lock the mutex
when it (finally) succeeds, it returns from the Wait Doesnt this sound complex? Why do we do
it? the idea is that the condition of the condition
variable depends on data protected by the mutex
-
7/27/2019 Intro Threads
14/29
Condition Variable Example
Mutex io_mutex;
Condition non_empty;
...
Consumer:
Lock (io_mutex) {while (port.empty())
Wait(io_mutex, non_empty);
process_data(port.first_in());
}
Producer:
Lock (io_mutex) {
port.add_data();
Signal(non_empty);
}
-
7/27/2019 Intro Threads
15/29
Couldnt We Do the Same with PlainCommunication?Mutex io_mutex;...
Consumer:
Lock (io_mutex) {
while (port.empty())
go_to_sleep(non_empty);process_data(port.first_in());
}
Producer:
Lock (io_mutex) {
port.add_data();
wake_up(non_empty);
}
Whats wrong with this? What if we dont lockthe mutex (or unlock it before going to sleep)?
-
7/27/2019 Intro Threads
16/29
Mutexes and Condition Variables
Mutexes and condition variables servedifferent purposes Mutex: exclusive access
Condition variable: long waits
Question: Isnt it weird to have both mutexes
and condition variables? Couldnt a singlemechanism suffice?
Answer:
-
7/27/2019 Intro Threads
17/29
Use of Mutexes and ConditionVariables
Protect shared mutable data:
void insert(int i) {
Element *e = new Element(i);
e->next = head;
head = e;
}
What happens if this code is run in twodifferent threads with no mutual
exclusion?
-
7/27/2019 Intro Threads
18/29
Using Condition Variables
Mutex io_mutex;
Condition non_empty;
...
Consumer:
Lock (io_mutex) {while (port.empty())
Wait(io_mutex, non_empty);
process_data(port.first_in());
}
Producer:
Lock (io_mutex) {
port.add_data();
Signal(non_empty);
}
Why use while instead of if? (think of manyconsumers, simplicity of coding producer)
-
7/27/2019 Intro Threads
19/29
Readers/WritersLocking
Mutex counter_mutex;
Condition read_phase,
write_phase;
int readers = 0;
Reader:
Lock(counter_mutex) {
while (readers == -1)
Wait(counter_mutex,
read_phase);
readers++;
}
... //read data
Lock(counter_mutex) {
readers--;
if (readers == 0)
Signal(write_phase);
}
Writer:
Lock(counter_mutex) {
while (readers != 0)
Wait(counter_mutex,
write_phase);
readers = -1;
}... //write data
Lock(counter_mutex) {
readers = 0;
Broadcast(read_phase);
Signal(write_phase);}
-
7/27/2019 Intro Threads
20/29
Comments on Readers/WritersExample
Invariant: readers >= -1
Note the use of Broadcast
The example could be simplified by using a single condition
variable for phase changes less efficient, easier to get wrong
Note that a writer signals all potential readers and one potentialwriter. Not all can proceed, however
(spurious wake-ups)
Unnecessary lock conflicts may arise (especially formultiprocessors):
both readers and writers signal condition variables while stillholding the corresponding mutexes
Broadcast wakes up many readers that will contend for a mutex
-
7/27/2019 Intro Threads
21/29
Readers/Writers Example
Reader: Writer:Lock(mutex) { Lock(mutex) {
while (writer) while (readers !=0 || writer)
Wait(mutex, read_phase) Wait(mutex, write_phase)
readers++; writer = true;
} }
// read data // write data
Lock(mutex) { Lock(mutex) {
readers--; writer = false;
if (readers == 0) Broadcast(read_phase);
Signal(write_phase); Signal(write_phase);
} }
-
7/27/2019 Intro Threads
22/29
Avoiding Unnecessary Wake-ups
Reader:
Lock(counter_mutex) {waiting_readers++;
while (readers == -1)
Wait(counter_mutex,
read_phase);
waiting_readers--;
readers++;}
... //read data
Lock(counter_mutex) {
readers--;
if (readers == 0)
Signal(write_phase);}
Writer:
Lock(counter_mutex) {while (readers != 0)
Wait(counter_mutex,
write_phase);
readers = -1;
}
... //write dataLock(counter_mutex) {
readers = 0;
if (waiting_readers > 0)
Broadcast(read_phase);
else
Signal(write_phase);}
Mutex counter_mutex;Condition read_phase, write_phase;
int readers = 0, waiting_readers =0;
-
7/27/2019 Intro Threads
23/29
Problems With This Solution
Explicit scheduling: readers always havepriority
may lead to starvation (if there are alwaysreaders)
fix: make the scheduling protocol morecomplicated than it is now
To Do: Think about avoiding the problem of waking
up readers that will contend for a singlemutex if executed on multiple processors
-
7/27/2019 Intro Threads
24/29
Discussion Example
Two kinds of threads, red and green, areaccessing a critical section. The critical sectionmay be accessed by at most three threads ofany kind at a time. The red threads havepriority over the green threads. Discuss the CS_enter and CS_exit code.
Why do we need the red_waiting variable?
Which condition variable should be signalledwhen?
Can we have only one condition variable?
-
7/27/2019 Intro Threads
25/29
Mutex m; Condition red_cond, green_cond;
int red_waiting = 0; int green = 0, red = 0;
Red:
Lock(m) {
// ???
while (green + red == 3)
Wait(m, red_cond);red++;
// ???
}
... //access data
Lock(m) {
red--;
// ???
// ???
// ???
}
Green:
Lock(m) {
while (green + red == 3
|| red_waiting != 0)
Wait(m, green_cond);green++;
}
... //access data
Lock(mutex) {
green--;
// ???
// ???
// ???
}
-
7/27/2019 Intro Threads
26/29
Deadlocks (brief)
Well talk more later for now beware of deadlocks
Examples:
A locks M1, B locks M2, A blocks on M2, B blocks on M1
Similar examples with condition variables and mutexes Techniques for avoiding deadlocks:
Fine grained locking
Two-phase locking: acquire all the locks youll ever needup front, release all locks if you fail to acquire any one
very good technique for some applications, butgenerally too restrictive
Order locks and acquire them in order (e.g., all threads firstacquire M1, then M2)
-
7/27/2019 Intro Threads
27/29
MT and fork() and Thread-Safelibraries
fork semantics forkall (e.g., Solaris fork()) forkone (e.g., Solaris fork1(), POSIX fork())
ensure no mutexes needed by child process areheld by other threads in parent process (e.g.,pthread_atfork)
Issue: mutex maybe be locked by parent at fork time ->child process will get its own copy of mutex with stateLOCKED and noone to unlock it!
not all libraries are thread-safe must check to ensure use may have thread-safe alternatives
-
7/27/2019 Intro Threads
28/29
Scope of multithreading
LWPs, kernel and user threads
kernel-level threads supported by the kernel Solaris, Linux, Windows XP/2000
all scheduling, synchronization, thread structures maintained inkernel
could write apps using kernel threads, but would have to go tokernel for everything
user-level threads supported by a user-level library
Pthreads, Java threads, Win32 sched. & synch can often be done fully in user space; kernel
doesnt need to know there are many user threads
problem with blocking on a system call
-
7/27/2019 Intro Threads
29/29
Light Weight Processes - LWP
These are virtual CPUs, can be multiple perprocess
The scheduler of a threads library schedulesuser-level threads to these virtual CPUs
kernel threads implement LWPs => visible tothe kernel, and can be scheduled
sometimes LWP & kernel threads usedinterchangeably, but there can be kernelthreads without LWPs