Threads - PowerPoint PPT Presentation

About This Presentation
Title:

Threads

Description:

Title: Threads Author: Roger deBry Last modified by: Faculty Created Date: 9/24/2001 7:30:36 PM Category: CNS 3060 Slides Document presentation format – PowerPoint PPT presentation

Number of Views:324
Avg rating:3.0/5.0
Slides: 81
Provided by: RogerD152
Category:
Tags: funny | slides | threads

less

Transcript and Presenter's Notes

Title: Threads


1
Unix Synchronization Mutexes
Semaphores - named - unnamed
2
First we will study thread synchronization in
Posix
Posix thread synchronization is done with mutex
locks
3
First, a quick review of threads ...
4
operating systems
Consider the following code
include ltstdio.hgt void print_msg(char) int
main ( ) print_msg(hello )
print_msg(world\n) return 0
void print_msg(char m) int i for (i
0 i lt 5 i) printf(s,
m) fflush( stdout )
sleep(1)
5
operating systems
This program will produce
hello hello hello hello hello world world world wo
rld world
6
The thread of execution is
operating systems
print_msg( )
main( )
7
With multiple threads of execution
operating systems
print_msg( )
main( )
8
void print_msg(void m) char cp (char)
m int i for (i 0 i lt 5 i)
printf(s, cp) fflush(
stdout ) sleep(1)
Consider the following code
operating systems
include ltstdio.hgt include ltpthread.hgt void
print_msg(void ) int main ( ) pthread
thr1, thr2 pthread_create(thr1,
NULL, print_msg, (void )hello )
pthread_create(thr2, NULL, print_msg, (void
)world\n ) pthread_join(thr1, NULL)
pthread_join(thr2, NULL) return 0
9
operating systems
This program will produce
hello world hello world hello world hello
world hello world
10
operating systems
the address of the thread
the function to run on the thread
pthread_create(thr1, NULL, print_msg, (void
)hello )
use default thread attributes
start a new thread of execution.
the function parameter
11
operating systems
the name of the thread
pthread_join(thr1, NULL)
where to store any return value
wait here for this thread to finish
12
operating systems
Thread Communication
One of the advantages of threads is that they
share the same address space. Threads can
communicate by reading and writing to that shared
space.
13
operating systems
static int globalValue 0 int main ( )
int i pthread_t mythread
pthread_create(mythread, NULL, funny_add,
NULL) for (i 0 i lt 10 i)
globalValue sleep(1)
pthread_join(mythread, NULL) printf(\nDone
...global value d\n, globalValue) return
0
our shared variable
14
void funny_add(void arg) int j for (j
0 j lt 10 j) int temp
globalValue 1 sleep(1)
globalValue temp
15
Using a shared variable
operating systems
funny_add( )
main( )
pthread_create
temp globalValue 1 sleep globalValue temp
globalValue sleep
pthread_join
globalValue
16
operating systems
This program seems to work okay, but it has a
potential problem .... what is it?
There is no thread synchronization. We cannot
guarantee the order in which the threads
are executed.
Lets Try It - exampleOne.c
17
Hmmmm.... Lets add some print statements so
that we can see how the threads are executing.
18
static int globalValue 0 int main ( )
int i pthread_t mythread
pthread_create(mythread, NULL, funny_add,
NULL) for (i 0 i lt 10 i)
fprintf(stderr, Main thread, globalValue
s\n, globalValue) globalValue
fprintf(stderr, Main thread, incr globalValue
s\n, globalValue) sleep(1)
pthread_join(mythread, NULL) printf(\nDone
...global value d\n, globalValue) return
0
our shared variable
operating systems
19
void funny_add(void arg) int j for (j
0 j lt 10 j) fprintf(stderr, in
funny_add, globalValue d\n, globalValue)
int temp gloablValue 1 sleep(1)
globalValue temp fprintf(stderr, in
funny_add, incr globalValue d\n,
globalValue)
20
Mutexes
  • A mutex is a special variable that can either be
    in a locked state or an unlocked state.
  • If a mutex is locked, it has a distinguished
    thread that holds or owns the mutex.
  • If no thread holds the mutex, it is unlocked,
    free, or available.
  • A mutex also has a queue for threads that are
    waiting to hold the mutex. The order in which the
    threads in the queue obtain the mutex is
    determined by the OSs thread scheduling policy.

21
  • The mutex is the simplest and most efficient
    thread synchronization mechanism.
  • A mutex is meant to be held for short periods of
    time.
  • A thread that is waiting for a mutex is not
    logically interruptible, except by termination of
    the process or termination of the thread.
  • Mutex locks are ideal for making changes to data
    structures in which the state of the data
    structure is temporarily inconsistent.

22
Creating and initializing a mutex
In posix mutexes are declared with the data type
pthread_mutex_t.
A program must always initialize a mutex before
it is used. A program can initialize a mutex in
one of two ways
23
Dynamic Initialization
returns 0 if successful
int pthread_mutex_init(pthread_mutex_t mymutex,
const
pthread_mutexattr_t attr)
pointer to a dynamically allocated region of
memory to store the mutex.
pointer to mutex attributes okay to pass NULL
when you initialize a mutex with
pthead_mutex_init, you must destroy the mutex
with pthread_mutex_destroy( ). Note that it
does not free the memory used to store the mutex.
24
Static Initialization
pthread_mutex_t mymutex PTHREAD_MUTEX_INITIALIZE
R
25
Locking and Unlocking a Mutex
int pthread_mutex_lock(pthread_mutex_t
mutex) int pthread_mutex_trylock(pthread_mute
x_t mutex) int pthread_mutex_unlock(pthread_m
utex_t mutex)
Blocks until the mutex is available
All return 0 if successful
Returns immediately if the mutex is not available
Releases the mutex
26
Lets synchronize our threads with a mutex!
27
static int globalValue 0 static
pthread_mutex_t mymutex PTHREAD_MUTEX_INITIALIZE
R int main ( ) int i pthread_t
mythread pthread_create(mythread, NULL,
funny_add, NULL) for (i 0 i lt 10 i)
pthread_mutex_lock(mymutex)
fprintf(stderr, Main thread, globalValue
s\n, globalValue) globalValue
fprintf(stderr, Main thread, incr globalValue
s\n, globalValue) pthread_mutex_unlock(m
ymutex) sleep(1)
pthread_join(mythread, NULL) printf(\nDone
...global value d\n, globalValue) return
0
operating systems
28
A Volunteer Effort
operating systems
Locking and unlocking are voluntary and a program
only achieves mutual exclusion when its threads
correctly acquire the appropriate mutex when
entering a critical section of code, and release
the mutex when finished. One way of ensuring
this is to only allow access to shared variables
through well defined functions. Then put the
mutex lock and unlock calls in these functions.
The locking mechanism is then transparent to the
calling threads.
29
Should I Always Use a Mutex?
operating systems
Consider a program containing a bunch of threads
that execute the instruction myGlobalValue
myGlobalValue 1 Since this statement will
likely generate a single machine instruction,
should we worry about using a mutex?
Yes, you should!
30
Why?
operating systems
Consider an SMP architecture that updates main
memory in 32-bit blocks. Each processor may
execute the instruction at the same time. The
memory modification will then trickle down
through level 1 and level 2 cache and then to
main memory. If you are incrementing a 64 bit
integer without mutexes, the uppermost 4 bytes
might come from one processor and the lower 4
bytes from the other. Bummer!
31
How Many Mutexes
operating systems
  • If you place too many mutexes your code will slow
    down considerably.
  • Use mutexes to serialize access to shared data.
    Dont use them for local data.
  • Dont use them if your program logic ensures that
    only one thread can access shared data at a
    single time.

32
Protecting Unsafe Library Functions
operating systems
Many C library functions are not thread-safe. An
example is the rand( ) function. However, you can
safely use the rand( ) function in a
multi-threaded environment if you can guarantee
that no two threads are concurrently calling it.
33
operating systems
int randsafe(double ranp) static
pthread_mutex_t lock
PTHREAD_MUTEX_INITIALIZER int error
if (error pthread_mutex_lock(lock))
return error ranp (rand( ) 0.5) /
(RAND_MAX 1) return pthread_mutex_unlock(
lock)
34
operating systems
Suppose that we want to count the number of
words in two separate files and add them
together, using two threads to do the counting
for us concurrently.
Molay ch 14
35
operating systems
Counting words with multiple threads of execution
count_words( )
main( )
word_count
word_count
word_count
file1
file2
36
operating systems
Suppose that the code generated for
word_count looks something like load
register, word_count add register, 1
store register, word_count
37
time
thread 1
operating systems
100
100
word_count
load reg, word_count
38
time
thread 1
thread 2
operating systems
100
word_count
load reg, word_count
100
load reg, word_count
add register, 1
101
store reg, word_count
39
time
thread 1
thread 2
operating systems
100
word_count
load reg, word_count
100
load reg, word_count
100
add register, 1
101
store reg, word_count
40
time
thread 1
thread 2
operating systems
101
word_count
load reg, word_count
100
load reg, word_count
100
add register, 1
101
store reg, word_count
add register, 1
101
store reg, word_count
41
time
thread 1
thread 2
operating systems
L O C K
100
word_count
load reg, word_count
100
this thread gets blocked because the shared
variable is locked
add register, 1
101
U N L O C K
101
store reg, word_count
now this thread can run --- and access the
shared variable word_count
word_count
42
operating systems
You can solve this problem without using a mutex!
Let each thread have its own counter. Add these
counters at the end.
43
operating systems
struct argset char fname / the file
name / int count / the word
count /
44
pthread_t thr1 struct argset args1 args1.fname
argv1 args1.count 0 . .
. pthread_create(thr1, NULL, count_words, (void
) args1) . . . //print the sum of the two
counters printf(5d total words\n, args1.count
args2.count)
pass in the struct as the argument
45
Now suppose your program is going to count the
words in a very big file and a very small
file. We would like to have some way for each
thread to notify the main thread when it is done
counting.
46
Using a Condition Variable
we signal that there is something in the
mailbox
the mailbox has a lock on it to prevent two
threads trying to access it at once.
47
Counting Thread 2
Counting Thread 1
Main Thread
set up a reporting mailbox - it has space
for one count at a time - it has a flag that
can be raised, it then snaps back - there is a
mutex lock to protect the mailbox
48
Counting Thread 2
Counting Thread 1
Main Thread
unlock the mailbox and wait until the flag goes
up
49
Counting Thread 2
Counting Thread 1
Main Thread
waits until it can lock the mailbox if the
mailbox is empty, it puts its count in the
mailbox It raises the flag on the mailbox
Then finally releases its lock
50
Counting Thread 2
Counting Thread 1
Main Thread
stops waiting when the flag goes up locks the
mailbox takes out the count processes the
count puts up the flag in case another count
thread is waiting unlocks the mailbox and waits
51
operating systems
struct argset char fname / the file
name / int count / the word
count /
52
struct argset mailbox NULL pthread_mutex_t
lock PTHREAD_MUTEX_INITIALIZER pthread_cond_t
flag PTHREAD_COND_INITIALIZER int main (int
argc, char argv ) pthread_t thr1, thr2
struct argset args1, args2 int reports_in
0 . . . pthread_mutex_lock(lock)
args1.fname argv1 args1.count 0 .
. . pthread_create(thr1, NULL, count_words,
(void ) args1)
declaring and initializing the condition variable
lock the mutex.
start a counting thread
53
One of the counting threads . . .
pthread_mutex_lock(lock)
The counting thread tries to get the lock. It
cant, so it blocks
54
Back in the main thread ...
while (reports_in lt 2) pthread_cond_wait(fla
g, lock)
this statement unlocks the mutex and blocks this
thread until the flag is signaled.
This is an atomic action!
55
One of the counting threads now gets the lock
pthread_mutex_lock(lock) if (mailbox !
NULL) pthread_cond_wait(flag, lock)
check to see if the mailbox is empty ... if it is
not, the thread has to wait until it is.
56
One of the counting threads now gets the lock
pthread_mutex_lock(lock) if (mailbox ! NULL)
pthread_cond_wait(flag, lock) mailbox
args
the mailbox is empty. store the address of this
threads arguments in the mailbox
57
One of the counting threads now gets the lock
pthread_mutex_lock(lock) if (mailbox ! NULL)
pthread_cond_wait(flag, lock) mailbox
args pthread_cond_signal(flag)
Raise the flag on the mailbox The main thread
is waiting for this signal!
58
One of the counting threads now gets the lock
pthread_mutex_lock(lock) if (mailbox ! NULL)
pthread_cond_wait(flag, lock) mailbox
args pthread_cond_signal(flag) pthread_mutex_u
nlock(lock)
Finally, unlock the mutex.
59
while (reports_in lt 2) pthread_cond_wait(fla
g, lock) total_words mailbox-gtcount
if (mailbox args1) pthread_join(thr1,
NULL) mailbox NULL . . .
control returns to this point when the condition
variable has been signaled and the mutex is
available. This call automatically re-locks the
mutex when it returns.
60
Semaphores
61
Semaphores
  • There are two types of semaphores
  • Binary semaphores work just like a mutex
  • Counting semaphores use when you have a finite
    set of resources that is being used by a set of
    threads. When a thread needs a resource, it
    decrements the associated semaphore. When it is
    done with the resource, the thread increments the
    associated semaphore. Semaphore values cannot
    fall below zero. When a thread tries to use a
    semaphore, it is blocked if the semaphore is zero.

62
Pseudo-code to illustrate how wait and signal work
void wait (semaphore_t sp) if (sp-gtvalue
gt 0) sp-gtvalue-- else //
sp-gtvalue is zero add this
thread to sp-gtlist block
void signal (semaphore_t sp) if
(sp-gtlist ! NULL) remove a thread from
the list and put it in ready state else
sp-gtvalue
These are atomic operations!
63
Semaphore Functions
for Posix un-named semaphores.
  • sem_init ( ) Creates and initializes a
  • semaphore
  • sem_wait ( ) decreases semaphore by one
  • if it is non-zero.
    Otherwise,
  • it waits
  • sem_post ( ) increases the semaphore by
  • one

Note OS X does not currently support unnamed
semaphores!
64
Semaphore Functions
for Posix named semaphores.
  • Declaring a Semaphore
  • sem_t mySemaphore
  • Initializing the Semaphore
  • sem_t sem_open (const char name, int
    flags, int perms, int value)

O_CREAT O_EXECL
The name of the semaphore Allows semaphores to
be used when not sharing memory.
S_IRUSR S_IWUSR
Initial value
65
Semaphore Functions
for Posix named semaphores.
  • Waiting on a Semaphore
  • int sem_wait(sem_t sem)
  • Signaling on a Semaphore
  • int sem_post(sem_t sem)

If a semaphore is 0, the calling thread blocks
until the semaphore becomes non-zero. Then it
decrements the semaphore.
If no threads are waiting, it increments the
semaphore. If one or more threads are
blocked, the value remains at zero and one of
the waiting threads is unblocked.
Both return 0 if successful
66
Semaphore Functions
for Posix named semaphores.
  • Removing the semaphore from the system
  • int sem_unlink(const char name)

Be sure that you include this function in your
code to unlink any semaphores! Otherwise they
persist beyond the lifetime of your program.
67
Circular buffer implementation of a bounded
queue for the consumer-producer problem
The sleeping Barber Problem
68
A classic Synchronization Problem
There is one Barber and one Barber Chair
There are n chairs in the waiting room
69
If there are no customers the barber sits in the
chair and goes to sleep.
70
If there are no customers the barber sits in the
chair and goes to sleep.
When a customer enters the barbershop, they sit
in the waiting room if there is a seat.
Otherwise they stand and wait for a chair.
71
If the Barbers chair is free, The customer wakes
the barber and sits in the chair.
When a customer enters the barbershop, they sit
in the waiting room if there is a seat.
Otherwise they stand and wait for a chair.
72
The sleeping Barber is a Consumer-Producer
Problem
73
consumers
queue
producers
producer and consumer threads share the queue and
must lock the resource when inserting or removing
items. When the queue is empty, consumers should
block until an item becomes available. If the
queue is full, producers should block until
there is space to insert an item
74
The Circular Queue Problem
bufin points to the next available slot
We need to make sure that a producer cannot write
data when the buffer is full, otherwise it will
write over existing good data.
We need to make sure that a consumer cannot
remove data if there is no data there.
Otherwise it will get garbage.
bufout points to the next item to be removed
75
Circular Queue
when accessing the buffer, use a mutex to make
sure that another thread is not accessing it at
the same time.
producer code
bufin points to the next available slot
void put_item(int item) pthread_mutex_lock(m
yLock) bufferbufin item bufin
(bufin 1) BUFSIZE pthread_mutex_unlock(my
Lock)
consumer code
void get_item(int item) pthread_mutex_lock(
myLock) item bufferbufout bufout
(bufout 1) BUFSIZE pthread_mutex_unlock(m
yLock)
bufout points to the next item to be removed
76
Initializing the semaphores
Un-named semaphores
declare the semaphore variables. items manages
the number of items in the buffer ready
for removal slots manages the number of buffer
positions available for data
sem_t items sem_t slots sem_init(items, 0,
0) sem_init(slots, 0, BUFSIZE)
The initial value. For items start at 0, no
items to remove
slots starts at BUFSIZE, all slots are
available
when this parameter is 0 the semaphore is local
to this process and can only be used by threads
in this process.
77
The Producer Thread
SUMSIZE is a globally defined constant that
controls the number of items processed.
static void producer(void arg1) int i
for (i 1 i lt SUMSIZE i )
sem_wait(slots) put_item(ii)
sem_post(items) return NULL
the producer thread tries to get a buffer
slot. If none is available (slots 0), then
the thread waits until one becomes available.
When a slot becomes available ( slots gt 0 )
sem_wait decrements slots and the thread proceeds
to execute.
after putting the data in the buffer, the
producer thread increments the count of
removable items by calling sem_post(items),
indicating that there is more data available for
removal..
78
The Consumer Thread
static void consumer(void arg2) int i,
myItem for (i 1 i lt SUMSIZE i )
sem_wait(items) get_item(myItem)
sem_post(slots) sum myitem
return NULL
the consumer thread tries to get available
data. If none is available (items 0), then
the thread waits until one becomes available.
When data becomes available ( items gt 0 ),
the the sem_wait call decrements the count
and the thread continues to execute..
after getting the data from the buffer, the
consumer thread increments the count of open
buffer slots by calling sem_post(slots).
79
Question?
Will this program work correctly if there is more
than one consumer thread?
static void consumer(void arg2) int i,
myItem for (i 1 i lt SUMSIZE i )
sem_wait(items) get_item(myItem)
sem_post(slots) sum myitem
return NULL
No!
All consumer threads will try to process SUMSIZE
items. Eventually the consumer threads will
block, since the producer thread only
produced SUMSIZE items,
80
The Producer Driven Problem
The problem is that once a consumer thread blocks
on a semaphore, there is no way to unblock the
consumer Except by incrementing the semaphore
with a sem_post. A producer who is finished
cannot do a sem_post without making the
consumers think that there is an item available
to be removed.
Write a Comment
User Comments (0)
About PowerShow.com