1 Synchronization Threads communicate to ensure consistency If not: race condition (non-deterministic result) Accomplished by synchronization operations.

Post on 19-Jan-2016

231 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

Transcript

1

Synchronization

Threads communicate to ensure consistency If not: race condition

(non-deterministic result) Accomplished by synchronization

operations

How to write concurrent code How to implement synchronization

operations

2

Synchronization – Motivation

“The too much milk problem”

Model of need to synchronize activities

3

Synchronization Terminology

Mutual exclusion (“mutex”) – prevents multiple threads from entering

Critical section – regions of code that modify or access shared variables

– code only one thread can execute at a time

Lock– mechanism for mutual exclusion Lock on entering critical section, accessing

shared data Unlock when complete Wait if locked

4

Solving the Too Much Milk Problem

Correctness properties Safety: “nothing bad happens” Progress: “something good

eventually happens”

First: use atomic loads & stores as building blocks “Leave a note” (lock) “Remove a note” (unlock) “Don’t buy milk if there’s a note” (wait)

5

Too Much Milk: Solution 1

thread A

if (no milk && no note)

leave note

buy milk

remove note

thread B

if (no milk && no note)

leave note

buy milk

remove note

Does this work?too much milk

6

Too Much Milk: Solution 2

thread A

leave note A

if (no note B)

if (no milk)

buy milk

remove note A

thread B

leave note B

if (no note A)

if (no milk)

buy milk

remove note B

Idea: use labeled notes

oops – no milk

7

Language Support

Synchronization complicated Better way – provide language-level

support Higher-level approach Hide gory details in runtime system

Increasingly high-level approaches: Locks, Atomic Operations Semaphores – generalized locks Monitors – tie shared data to

synchronization

8

Locks

Provide mutual exclusion to shared data via two atomic routines Lock::Acquire – wait for lock, then take

it Lock::Release – unlock, wake up waiters

Rules: Acquire lock before accessing shared

data Release lock afterwards Lock – initially released

9

Too Much Milk: Locks

Clean, symmetric - but how do we implement it?

thread A

Lock.acquire()

if (no milk)

buy milk

Lock.release()

thread B

Lock.acquire()

if (no milk)

buy milk

Lock.release()

10

Implementing Locks

Requires hardware support (in general)

Can build on atomic operations: Disable interrupts

Uniprocessors only Test & Set, Compare & Swap

11

Disabling Interrupts

Prevent scheduler from switching threads in middle of critical sections Ignores quantum expiration (timer

interrupt) No handling I/O operations

(Don’t make I/O calls in critical section!)

To ensure current sequence of instructions run atomically

Drawback?

12

Atomic Read-Write-Modify Instructions Atomically read old value, write

new value

Examples: Test & Set (most arch) Exchange (x86) Compare & Swap (68K, Sparc)

13

Implementing Locks: Test & Set

int testset (int value) { int old = value; value = 1; return old;}

pseudo-code: red = atomic

class Lock { private int value; Lock() { value = 0; } void acquire() {…} void release() {…}}

void acquire() { while (testset(value)) {}}

void release() { value = 0;}

Effect of testset(value) when value = 0? or 1?

acquire – wait until lock released, then take it

release – release lock value: 1 (locked); 0

(unlocked)

14

Busy Waiting (“Spinning”)

What’s wrong with this implementation? CPU utilization? Different

priorities?

void acquire() { while (testset(value))

{}}

void release() { value = 0;}

spin-lock

15

Minimizing Busy Waiting

Can’t implement locks with test & set without any waiting (w/o disabling interrupts) Add queue to lock and sleep: blocking

lockvoid acquire() { while (1) { if (testset(value)) { put thread on queue, sleep } else { break; } } }

void release() { value = 0; wake up threads;}

16

Locks in POSIX

pthread_mutex_t my_mutex;pthread_mutex_init(&my_mutex);// also: pthread_mutex_t my_mutex = PTHREAD_MUTEX_INITIALIZER;...pthread_mutex_lock(&my_mutex);x = x + 1; /* This is the critical section */pthread_mutex_unlock(&my_mutex);...pthread_mutex_destroy(&my_mutex);

pthread_mutex_t my_mutex;pthread_mutex_init(&my_mutex);// also: pthread_mutex_t my_mutex = PTHREAD_MUTEX_INITIALIZER;...pthread_mutex_lock(&my_mutex);x = x + 1; /* This is the critical section */pthread_mutex_unlock(&my_mutex);...pthread_mutex_destroy(&my_mutex);

17

Locks in POSIX: Example

Reader-writer problem: Share a buffer which holds one item (an

integer) A single reader and writer Reader: only read when there is item in

buffer Writer: only write when there is space in

buffer

18

Locks in POSIX: Example - II#include <pthread.h>

#include <stdio.h>

int buffer_has_item = 0;

int buffer;

pthread_mutex_t mutex;

void * reader_function(void *)

{

while(1){

pthread_mutex_lock( &mutex );

if ( buffer_has_item == 1) {

printf("reader consumes one item: %d.\n", buffer);

buffer_has_item = 0;

}

pthread_mutex_unlock( &mutex );

}

}

19

Locks in POSIX: Example - IIIvoid writer_function(void)

{

while(1) {

pthread_mutex_lock( &mutex );

if ( buffer_has_item == 0 ){

buffer = rand()*100;

printf("writer produces one item: %d\n", buffer);

buffer_has_item = 1;

}

pthread_mutex_unlock( &mutex );

}

}

void main()

{

pthread_t reader;

pthread_mutex_init(&mutex, NULL);

pthread_create( &reader, NULL, reader_function,NULL);

writer_function();

}

20

Locks as Synch Primitive

+ Locks provide mutual exclusion+ Only one thread enters section at a time+ Simplifies writing concurrent programs

- Low-level- Can complicate coding

- e.g., “allow at most n threads to access resource”

What are some alternatives?

21

Locks & Semaphores

Implementing locks Semaphores Monitors

22

Semaphores

What’s a “semaphore” anyway?

A visual system for sending information by means of two flags that are held one in each hand, using an alphabetic code based on the position of the signaler's arms.

23

Semaphores

What’s a “semaphore” anyway?

A visual signaling apparatus with flags, lights, or mechanically moving arms.

24

Semaphores in CS

Computer science: Dijkstra (1965)

A non-negative integer counter with atomic increment & decrement.  Blocks rather than going negative. Higher-level than locks but not too high level

25

Semaphores: Key Concepts

P(sem), a.k.a. wait = decrement counter

If sem = 0, block until greater than zero

P = “prolagen” (proberen te verlagen, “try to decrease”)

In Holland the good Dr. DijkstraTook a break from a walk on his bijkstra

And said: "Which shall it be?Take a P or a V?

For the two seem to me quite alijkstra!"

V(sem), a.k.a. signal = increment counter

Wake 1 waiting process

V = “verhogen” (“increase”)

26

Implementing Semaphores

class Semaphore { private int value; private Queue q; Semaphore(int v) { value = v; }}

void wait() { if (value > 0) { value = value – 1; }

if (value == 0) { add this process to Q; sleep(); } }

void signal() { value = value + 1; if (anyone on Q) { remove P from Q; wakeup(P); } }

27

Variants of Semaphores Binary semaphore

just two values (0 or 1), typically initial value 1 (“free”)

Counting semaphore useful when units of resource are

available initialized to number of resources thread can access as long as one

unit available

28

Binary Semaphores: Example

“too much milk” with locks

thread A

Lock.acquire()

if (no milk)

buy milk

Lock.release()

thread B

Lock.acquire()

if (no milk)

buy milk

Lock.release()

thread A

sem.wait()

if (no milk)

buy milk

sem.signal()

thread B

sem.wait()

if (no milk)

buy milk

sem.signal()

“too much milk” with binary semaphores(initially 1)

29

Binary Semaphore: Example

More flexible than locks! By initializing semaphore to 0,

threads can wait for an event to occur

thread A

// wait for thread B

sem.wait();// do stuff …

thread B

// do stuff, then

// wake up A

sem.signal();

30

Counting Semaphores: Example

Controlling resources: Allow threads to use at most 5 files simultaneously

Initialize to 5

thread A

sem.wait();// use a file

sem.signal();

thread B

sem.wait();// use a file

sem.signal();

31

Semaphore in POSIX

sem_t sem;sem_init(&sem, int pshared, unsigned value);

sem_wait(&sem);sem_post(&sem);

sem_destroy(&sem);

sem_t sem;sem_init(&sem, int pshared, unsigned value);

sem_wait(&sem);sem_post(&sem);

sem_destroy(&sem);

32

Solving Reader-writer Problem Using Semaphore

Reader-writer problem: Share a buffer which holds one item (an

integer) A single reader and writer Reader: only read when there is item in

buffer Writer: only write when there is space

in buffer How to make the writer enter the

critical section first? How to make the reader & writer

alternate to enter critical section?

33

Semaphore in Linux: Solving Reader-writer Problem#include <stdio.h>

#include <stdlib.h>

#include <pthread.h>

#include <semaphore.h>

sem_t readers_turn, writers_turn; /* semaphore declaration */

int buffer, loopcnt = 5;

void * reader_function(void *)

{

int i;

for (i=0; i<loopcnt; i++) {

sem_wait( &readers_turn );

printf("reader consumes one item: %d.\n", buffer);

sem_post( &writers_turn );

}

}

void writer_function(void) {

int i;

for (i=0; i<loopcnt; i++) {

sem_wait( &writers_turn );

buffer = rand();

printf("writer produces one item: %d\n", buffer);

sem_post( &readers_turn );

}

}

34

Semaphore in Linux: Solving Reader-writer Problem - IIint main()

{

pthread_t reader;

if (sem_init(&readers_turn, 0, 0) < 0) {

perror("sem_init");

exit(1);

}

if (sem_init(&writers_turn, 0, 1) < 0) {

perror("sem_init");

exit(1);

}

if(pthread_create( &reader, NULL, reader_function, NULL) != 0) {

perror("pthread_create");

exit(1);

}

writer_function();

sem_destroy(&readers_turn);

sem_destroy(&writers_turn);

return 1;

}

35

Summary

Implementing locks Test & Set Spin locks, blocking locks

Semaphores Generalization of locks Binary, counting (“Dijkstra-style”) Useful for:

Controlling resources Waiting on events

top related