35
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

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

Embed Size (px)

Citation preview

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

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

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

2

Synchronization – Motivation

“The too much milk problem”

Model of need to synchronize activities

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

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

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

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)

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

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

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

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

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

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

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

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

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

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()

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

10

Implementing Locks

Requires hardware support (in general)

Can build on atomic operations: Disable interrupts

Uniprocessors only Test & Set, Compare & Swap

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

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?

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

12

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

new value

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

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

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)

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

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

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

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;}

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

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);

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

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

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

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 );

}

}

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

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();

}

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

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?

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

21

Locks & Semaphores

Implementing locks Semaphores Monitors

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

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.

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

23

Semaphores

What’s a “semaphore” anyway?

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

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

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

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

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”)

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

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); } }

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

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

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

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)

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

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();

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

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();

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

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);

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

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?

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

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 );

}

}

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

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;

}

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

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