In Lock::Async§
See primary documentation in context for method protect
method protect(Lock::Async:D: &code)
This method reliably wraps code passed to &code
parameter with a lock it is called on. It calls lock
, does an await
to wait for the lock to be available, and reliably calls unlock
afterwards, even if the code throws an exception.
Note that the Lock::Async
itself needs to be created outside the portion of the code that gets threaded and it needs to protect. In the first example below, Lock::Async
is first created and assigned to $lock
, which is then used inside the Promise
s code to protect the sensitive code. In the second example, a mistake is made, the Lock::Async
is created right inside the Promise
, so the code ends up with a bunch of different locks, created in a bunch of threads, and thus they don't actually protect the code we want to protect. Modifying an Array simultaneously from different in the second example is not safe and leads to memory errors.
# Compute how many prime numbers there are in first 10 000 of them # using 50 threads my @primes = 0 .. 10_000; my @results; my @threads; # Right: $lock is instantiated outside the portion of the # code that will get threaded and be in need of protection, # so all threads share the lock my $lock = Lock::Async.new; for ^50 -> $thread { @threads.push: start { $lock.protect: { my $from = $thread * 200; my $to = ($thread + 1) * 200; @results.append: @primes[$from..$to].map(*.is-prime); } } } # await for all threads to finish calculation await Promise.allof(@writers); # say how many prime numbers we found say "We found " ~ @results.grep(*.value).elems ~ " prime numbers";
The example below demonstrates the wrong approach: without proper locking this code will work most of the time, but occasionally will result in bogus error messages or low-level memory errors:
# !!! WRONG !!! Lock::Async is instantiated inside threaded area, # so all the 20 threads use 20 different locks, not syncing with # each other for ^50 -> $thread { @threads.push: start { my $lock = Lock::Async.new; $lock.protect: { my $from = $thread * 200; my $to = ($thread + 1) * 200; @results.append: @primes[$from..$to].map(*.is-prime); } } }
In Lock§
See primary documentation in context for method protect
multi method protect(Lock:D: &code)
Obtains the lock, runs &code
, and releases the lock afterwards. Care is taken to make sure the lock is released even if the code is left through an exception.
Note that the Lock
itself needs to be created outside the portion of the code that gets threaded and it needs to protect. In the first example below, Lock
is first created and assigned to $lock
, which is then used inside the Promise
s to protect the sensitive code. In the second example, a mistake is made: the Lock
is created right inside the Promise
, so the code ends up with a bunch of separate locks, created in a bunch of threads, and thus they don't actually protect the code we want to protect.
# Right: $lock is instantiated outside the portion of the # code that will get threaded and be in need of protection my $lock = Lock.new; await ^20 .map: { start { $lock.protect: { print "Foo"; sleep rand; say "Bar"; } } } # !!! WRONG !!! Lock is created inside threaded area! await ^20 .map: { start { Lock.new.protect: { print "Foo"; sleep rand; say "Bar"; } } }