B3CC: Concurrency 03: Threads Trevor L. McDonell Utrecht - - PowerPoint PPT Presentation

b3cc concurrency 03 threads
SMART_READER_LITE
LIVE PREVIEW

B3CC: Concurrency 03: Threads Trevor L. McDonell Utrecht - - PowerPoint PPT Presentation

B3CC: Concurrency 03: Threads Trevor L. McDonell Utrecht University, B2 2020-2021 Announcement The first practical assignment has been released - http://www.cs.uu.nl/docs/vakken/b3cc/assignments.html - Deadline: 2020-11-28 @ 23:59 2


slide-1
SLIDE 1

B3CC: Concurrency 03: Threads

Trevor L. McDonell Utrecht University, B2 2020-2021

slide-2
SLIDE 2

Announcement

  • The first practical assignment has been released
  • http://www.cs.uu.nl/docs/vakken/b3cc/assignments.html
  • Deadline: 2020-11-28 @ 23:59

2

slide-3
SLIDE 3

Mutual Exclusion

3

slide-4
SLIDE 4

Recall: concurrent access to a global queue

  • Thread A:
  • Create new object
  • Set last->.next to &new
  • Set last to &new
  • Thread B:
  • Create new object
  • Set last->.next to &new
  • Set last to &new

4

head last

slide-5
SLIDE 5

Mutual exclusion

  • Mutual exclusion (locking) protects shared resources
  • Only one process at a time is allowed to access the critical resource
  • Modifications to the resource appear to happen atomically
  • In this lecture: a software approach implementing concurrency control
  • That is, without relying on builtin language features or hardware support

(we’ll cover that later)

5

slide-6
SLIDE 6

Software approach to mutual exclusion

  • Premise
  • One or more threads with shared memory
  • Elementary mutual exclusion at the level of memory access
  • Simultaneous access to the same memory location are serialised
  • Requirements for mutual exclusion
  • Only one thread at a time is allowed in the critical section
  • No deadlock or starvation

6

slide-7
SLIDE 7

Attempt #1

  • The plan:
  • Threads take turns executing the critical section
  • Exploit serialisation of memory access to implement serialisation of access

to the critical section

  • Employ a shared variable (memory location) turn that indicates whose turn it

is to enter the critical section

7

while (turn !!> 0) //+ do nothing **0 ; <critical section> turn = 1; while (turn !!> 1) //+ do nothing **0 ; <critical section> turn = 0;

P0: P1:

slide-8
SLIDE 8

Attempt #1

  • Busy waiting (spin lock)
  • Process is always checking to see if it can enter the critical section
  • Implements mutual exclusion
  • Simple
  • Disadvantages
  • Process burns resources while waiting
  • Processes must alternate access to the critical section
  • If one process fails anywhere in the program, the other is permanently

blocked

8

slide-9
SLIDE 9

Attempt #2

  • The problem:
  • turn stores who can enter the critical section, rather than whether anybody

may enter the critical section

  • The new plan:
  • Store for each process whether it is in the critical section right now
  • flag[i] if process i is in the critical section

9

while (flag[1]) //+ do nothing **0 ; flag[0] = true; <critical section> flag[0] = false; while (flag[0]) //+ do nothing **0 ; flag[1] = true; <critical section> flag[1] = false;

P0: P1:

slide-10
SLIDE 10

Attempt #2

10

  • If a process fails?
  • Outside the critical section: the other is not blocked
  • Inside the critical section: the other is blocked (however, difficult to avoid)
  • Does it work?
  • 1. Both flags are set to false
  • 2. P0 enters critical section
  • 3. P1 enters critical section
  • 4. P1 sets flag[1]
  • 5. P0 sets flag[0]
  • Does not guarantee exclusive access
slide-11
SLIDE 11

Attempt #3

  • The goal:
  • Remove the gap between toggling the two flags
  • The new updated plan:
  • Move setting the flag to before checking whether we can enter

11

flag[0] = true; while (flag[1]) //+ do nothing **0 ; <critical section> flag[0] = false; flag[1] = true; while (flag[0]) //+ do nothing **0 ; <critical section> flag[1] = false;

P0: P1:

slide-12
SLIDE 12

Attempt #3

  • Is it working now?
  • No. The gap can cause a deadlock now >_>
  • Deadlock: when each member of a group of processes is waiting for another to

take action (e.g. waiting for another to release a lock)

12

slide-13
SLIDE 13

Attempt #4

  • Previous problem:
  • Process sets its own state before knowing the other processes’ states, and

cannot back off

  • The new updated revised plan:
  • Process retracts its decision if it cannot enter

13

flag[0] = true; while (flag[1]) { flag[0] = false; delay(); flag[0] = true; } <critical section> flag[0] = false; flag[1] = true; while (flag[0]) { flag[1] = false; delay(); flag[1] = true; } <critical section> flag[1] = false;

P0: P1:

slide-14
SLIDE 14

Attempt #4

  • Is it working now?
  • Close, but we may have a livelock =_=
  • Livelock:

The states of the group of processes are constantly changing with regard to each other, but none are progressing (e.g. trying to obtaining a lock, but backing

  • ff if it fails)
  • A special case of resource starvation, and a risk for algorithms which

attempt to detect and recover from deadlock

14

slide-15
SLIDE 15

Attempt #5

  • Improvements
  • We can solve this problem by combining the fourth and first attempts
  • In addition to the flags we use a variable indicating whose turn it is to

have precedence in entering the critical section

15

slide-16
SLIDE 16

Attempt #5: Peterson’s algorithm

  • Both processes are courteous and solve a tie in favour of the other
  • Algorithm can be generalised to work with n processes

16

flag[0] = true; turn = 1; while (flag[1] &&' turn ==> 1) //+ do nothing **0 ; <critical section> flag[0] = false; flag[1] = true; turn = 0; while (flag[0] &&' turn ==> 0) //+ do nothing **0 ; <critical section> flag[1] = false;

P0: P1:

slide-17
SLIDE 17

Attempt #5: Peterson’s algorithm

  • Statement: mutual exclusion


Threads 0 and 1 are never in the critical section at the same time

  • Proof:
  • If P0 is in the critical section then
  • flag[0] is true
  • flag[1] is false OR turn is zero OR P1 is trying to enter the critical

section, after setting flag[1] to true but before setting turn to zero

  • For both P0 and P1 to be in the critical section
  • flag[0] AND flag[1] AND turn=0 AND turn=1

17

slide-18
SLIDE 18

Hardware support

  • The compare-and-swap (CAS) operation is an atomic instruction which allows

mutual exclusion for any number of threads using a single bit of memory

  • Compares the memory location to a given value
  • If they are the same, writes a new value to that location
  • Returns the old value of the memory location

18

https://www.felixcloutier.com/x86/cmpxchg

slide-19
SLIDE 19

Hardware support

  • The plan:
  • Use a bit lock where zero represents unlocked and one represents locked
  • In Haskell we can use atomicModifyIORef’ or

atomicModifyIORefCAS (from atomic-primps)

19

while (atomic_cas(lock, 0, 1) ==> 1) //+ do nothing **0 ; <critical section> lock = 0;

slide-20
SLIDE 20

Non-blocking algorithms

  • Obstruction-freedom
  • From any point after a thread begins executing in isolation, it finishes in a

finite number of steps (a thread will be able to finish if no other thread makes progress)

  • Lock-freedom
  • Some method will finish in a finite number of steps
  • Wait-freedom
  • Every method will finish in a finite number of steps
  • Starvation
  • A process is denied access to a resource. Antonym: fairness

20

slide-21
SLIDE 21

Non-blocking algorithms

21

http://www.cs.tau.ac.il/~shanir/progress.pdf

Wait-free Lock-free Obstruction-free Starvation-free Deadlock-free ?

every method makes progress some method makes progress maximal vs. mimimal dependent vs. independent blocking vs. non-blocking independent non-blocking dependent non-blocking dependent blocking

slide-22
SLIDE 22

Threads in Haskell

22

slide-23
SLIDE 23

Threads

  • The fundamental action in concurrency: create a new thread of control
  • Takes a computation of type IO () as its argument
  • This IO action executes in a new thread concurrently with other threads
  • No specified order in which threads execute
  • Threads are very cheap: ~1.5 Kb / thread, easily run thousands of threads

23

forkIO ::; IO () ->. IO ThreadId

slide-24
SLIDE 24

Example

  • Interleaving of two threads

24

import Control.Concurrent import Control.Monad main ::; IO () main = do let n = 1000 forkIO $ replicateM_ n (putChar 'A') forkIO $ replicateM_ n (putChar 'B') return ()

slide-25
SLIDE 25

Example

  • Interleaving of two threads
  • The term n ::; Int is shared between both threads (captured); this is

safe because it is immutable

  • The program exits when main returns, even if there are other threads still

running!

  • How to check whether the child thread has completed?

25

slide-26
SLIDE 26

tot ziens

Photo by Ugur Arpaci