0% found this document useful (0 votes)
29 views32 pages

Concurrency and More

The document discusses synchronization in concurrent programs using semaphores. It describes how semaphores can be used to enforce synchronization constraints like mutual exclusion and barriers. Semaphores allow threads to block until certain conditions are met, preventing race conditions on shared resources. Specific patterns like mutex, multiplex, and barriers are explained with examples of how semaphores implement synchronization between threads.

Uploaded by

Mohamad Yassine
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
29 views32 pages

Concurrency and More

The document discusses synchronization in concurrent programs using semaphores. It describes how semaphores can be used to enforce synchronization constraints like mutual exclusion and barriers. Semaphores allow threads to block until certain conditions are met, preventing race conditions on shared resources. Specific patterns like mutex, multiplex, and barriers are explained with examples of how semaphores implement synchronization between threads.

Uploaded by

Mohamad Yassine
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 32

More on Concurrency

Dr. Mageda Sharafeddin


Synchronization
• “synchronization” means making two things happen at the same time.
• In computer systems, synchronization refers to relationships among events any
number of events, and any kind of relationship (before, during, after).
• Computer programmers are often concerned with synchronization constraints.
Examples include:
• Serialization: Event A must happen before Event B.
• Mutual exclusion: Events A and B must not happen at the same time.
Semaphore for Synchronization
• In real life we often check and enforce synchronization constraints using a clock. How
do we know if A happened before B?
• In computer systems, we often need to satisfy synchronization constraints without the
benefit of a clock, either because there is no universal clock, or because we don’t
know with fine enough resolution when events occur.
• Semaphore: used in software techniques for enforcing synchronization constraints.
Program Models
• Sequential: If Statement A comes before Statement B, it will be executed first.
• There are two ways things get more complicated. One possibility is that the computer
is parallel, meaning that it has multiple processors running at the same time. In that
case it is not easy to know if a statement on one processor is executed before a
statement on another.
• Another possibility is that a single processor is running multiple threads of execution.
A thread is a sequence of instructions that execute sequentially. If there are multiple
threads, then the processor can work on one for a while, then switch to another, and
so on.
Synchronization
• In general the programmer has no control over when each thread runs; the operating
system (specifically, the scheduler) makes those decisions. As a result, again, the
programmer can’t tell when statements in different threads will be executed.
• For purposes of synchronization, there is no difference between the parallel model
and the multithread model. The issue is the same—within one processor (or one
thread) we know the order of execution, but between processors (or threads) it is
impossible to tell.
Synchronization
• Imagine that you and your friend Bob live in different cities, and one day, around
dinner time, you start to wonder who ate lunch first that day, you or Bob. How would
you find out? Obviously you could call him and ask what time he ate lunch. But what
if you started lunch at 11:59 by your clock and Bob started lunch at 12:01 by his
clock? Can you be sure who started first? Unless you are both very careful to keep
accurate clocks, you can’t.
• Computer systems face the same problem because, even though their clocks are
usually accurate, there is always a limit to their precision. In addition, most of the time
the computer does not keep track of what time things happen. There are just too
many things happening, too fast, to record the exact time of everything.
You want to make sure you eat lunch before Bob

Order if events A1<A2<A3<A4 & B1<B2<B3


And A4<B2
Concurrent Events
• Two events are concurrent if we cannot tell by looking at the program which will
happen first.
• Concurrent programs are often non-deterministic.

• During any given run of this program, the output might be “yes no” or “no yes”.
• Concurrent programs are hard to debug.
Shared Variables
• Most variables are local meaning that they belong to a single thread and no other
threads can access them.
• Shared among two or more threads; this is one of the ways threads interact with each
other. For example, one way to communicate information between threads is for one
thread to read a value written by another thread.
• Other ways that threads interact are concurrent writes (two or more writers)
• Concurrent updates (two or more threads performing a read followed by a write).
Concurrent writes

• Puzzle: What path yields output 5 and final value 5?


• Puzzle: What path yields output 7 and final value 7?
• Puzzle: Is there a path that yields output 7 and final value 5? Can you prove it?
Concurrent updates

• Is there a synchronization problem?


Concurrent updates

• Execution1: A1<B1<B2<A2
• Execution2: A1<A2<B1<B2
Semaphores
• Semaphore is a data structure that is useful for solving a variety of synchronization problems.
• A semaphore is like an integer, with three differences:
1. When you create the semaphore, you can initialize its value to any integer, but after that the
only operations you are allowed to perform are increment (increase by one) and decrement
(decrease by one). You cannot read the current value of the semaphore.
2. When a thread decrements the semaphore, if the result is negative, the thread blocks itself
(notifies the scheduler that it cannot proceed.) and cannot continue until another thread
increments the semaphore.
3. When a thread increments the semaphore, if there are other threads waiting, one of the
waiting threads gets unblocked.
Semaphores
• What the value of the semaphore means.
• If the value is positive, then it represents the number of threads that can decrement
without blocking.
• If it is negative, then it represents the number of threads that have blocked and are
waiting.
• If the value is zero, it means there are no threads waiting, but if a thread tries to
decrement, it will block.
Semaphores
read = Semaphore(1);
read.increment(); or read.signal();
read.decrement(); read.wait();
Or
read.V();
read.P();
Semaphore Patterns (signaling)
Thread A Thread B
1. Statement a; 1. Sem.wait();
2. Sem.signal(); 2. Statement b;
Semaphore Patterns (Rendezvous)
Thread A Thread B
1. Statement a1; 1. Statement b1;
2. Statement a2; 2. Statement b2;
We want to guarantee that a1 happens before b2 and b1 happens
before a2.
Semaphore Patterns (Rendezvous)
Thread A Thread B
1. Statement a1; 1. Statement b1;
2. AdoneSema.signal(); 2. BdoneSema.signal();
3. BdoneSema.wait(); 3. AdoneSema.wait();
4. Statement a2; 4. Statement b2;
What is the difference between the two?
Mutex: Semaphore for enforcing mutual
exclusion
Thread A Thread B
count = count + 1; count = count + 1;
How do we solve this problem?
Mutex Solution

Thread A Thread B
mutex.wait(); mutex.wait();
count = count + 1; count = count + 1;
mutex.signal(); mutex.signal();
Multiplex: Allow Multiple Threads
Thread A Thread B Thread C
mutex.down(); mutex.down(); mutex.down();
count = count + 1; count = count + 1; count = count + 1;
mutex.up(); mutex.up(); mutex.up();

Note we are using up/down instead of signal/wait just to make it easier


to understand that a count is involved. Otherwise, they are identical.
Barrier: Generalizing the Rendezvous Problem
Thread A Thread B Thread C
statement a1 statement b1; statement c1;
statement a2; statement b2; statement c2;

Statement a2 should only execute if both statements b1 and c1 were executed.


Similarly statement b2 should only execute of both a1 and c1 were executed and
statement c2 should only be executed if both a1 and b2 were executed.
Therefore there is an implicit barrier after the statement.
Barrier: Generalizing the Rendezvous Problem
Thread A Thread B Thread C
statement a1 statement b1; statement c1;
barrier(); barrier(); barrier();
statement a2; statement b2; statement c2;
Barrier
n = the number of threads
count = 0; //count keeps track of how many threads have arrived.
mutex = Semaphore (1); //mutex provides exclusive access to count
barrier = Semaphore(0); //barrier is locked (zero or negative) until all
threads arrive then it should be unlocked (1 or more).
Barrier: Non Solution
rendezvous
mutex.wait () kl wahad nater dawro

count = count + 1 increment

mutex.signal () release

full if (count == n) barrier.signal () //consider 1 thread here


barrier.wait () //several threads here
critical point
Barrier: Solution
rendezvous
mutex . wait ()
count = count + 1
mutex . signal ()
if (count == n) barrier.signal ()
barrier.wait ()
barrier.signal ()
critical point
Reusable Barrier
• Often a set of cooperating threads will perform a series of steps in a loop
and synchronize at a barrier after each step. For this application we need a
reusable barrier that locks itself after all the threads have passed through.
Reusable Barrier: Non Solution
Rendezvous
mutex.wait ()
count += 1
mutex.signal ()
if count == n: turnstile.signal ()
turnstile.wait ()
turnstile.signal ()
critical point
mutex.wait ()
count -= 1
mutex.signal ()
if count == 0: turnstile.wait ()
Reusable Barrier: Non Solution
rendezvous
mutex.wait ()
count += 1
if count == n: turnstile.signal ()
mutex.signal ()
turnstile.wait ()
turnstile.signal ()
critical point
mutex.wait ()
count -= 1
if count == 0: turnstile.wait ()
mutex.signal ()
Reusable Barrier: Solution-two phase barrier
• turnstile = Semaphore (0)
• turnstile2 = Semaphore (1)
• mutex = Semaphore (1)
• Force all the threads to wait twice: once for all the threads to arrive and again
for all the threads to execute the critical section.
• Only the nth thread can lock or unlock the turnstiles.
• Before a thread can unlock the first turnstile, it has to close the second, and
vice versa; therefore it is impossible for one thread to get ahead of the others
by more than one turnstile.
Reusable Barrier: Solution
# rendezvous mutex.wait ()
mutex.wait () count -= 1
count += 1 if count == 0 turnstile.wait () # lock the first
if count == n turnstile2.wait () # lock the second turnstile2.signal () # unlock the second
turnstile.signal () # unlock the first mutex.signal ()
mutex.signal () turnstile2.wait () # second turnstile
turnstile.wait () # first turnstile turnstile2.signal ()
turnstile.signal ()

You might also like