Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Mutexes

When two threads change the same variable, updates get lost. acct->balance += 1 is three steps (read, add, write back), and two threads can both read the same value, both add one, and both write it: two deposits, one recorded. A mutex makes a section of code run in one thread at a time.

#cmdline "-mt"

'' The shared total and the lock that guards it travel together.
type Account
    as any ptr lock
    as int64 balance
end type

sub Deposit( byval p as any ptr )
    var acct = cptr( Account ptr, p )
    for i as int32 = 1 to 100000
        mutexlock( acct->lock )       '' one thread at a time from here...
        acct->balance += 1
        mutexunlock( acct->lock )     '' ...to here
    next
end sub

dim acct as Account
acct.lock = mutexcreate( )

dim tellers( 0 to 3 ) as any ptr
for i as int32 = 0 to 3
    tellers( i ) = threadcreate( @Deposit, @acct )
next
for i as int32 = 0 to 3
    threadwait( tellers( i ) )
next

mutexdestroy( acct.lock )             '' no thread can use it any more
print "balance:"; acct.balance

Output

balance: 400000

Four tellers each deposit 1 a hundred thousand times, and the balance is exactly 400000. Between mutexlock and mutexunlock only one thread runs, so each += 1 happens whole. Without those two lines the balance comes out short, by a different amount on each run, and nothing reports an error.

CallDoes
mutexcreate( )makes a mutex and returns its handle, an any ptr
mutexlock( m )waits until no other thread holds m, then holds it
mutexunlock( m )lets the next waiting thread in
mutexdestroy( m )frees it, once no thread can use it any more

Keep the lock and the data it protects together, as Account does, so every procedure that can reach the data can reach its lock. Hold a mutex for as little code as you can: while one thread holds it, the others wait, and four threads taking turns on one lock run no faster than one.

Every mutexlock needs its mutexunlock on every path out, including an early exit or return. A thread that leaves while holding the lock makes every other thread wait for ever.

Activities

  1. Remove the mutexlock and mutexunlock lines and run the program a few times.
  2. Move the lock outside the loop, so each teller holds it for all its deposits. Is the result still right? What did the threads gain?