Atomic updates: Difference between revisions
Content added Content deleted
Thundergnat (talk | contribs) (Rename Perl 6 -> Raku, alphabetize, minor clean-up) |
|||
Line 2,420: | Line 2,420: | ||
Current sum: <same number, stays fixed></pre> |
Current sum: <same number, stays fixed></pre> |
||
This simply uses a variable named <tt>comp</tt> to determine whether or not it is currently computing something. |
This simply uses a variable named <tt>comp</tt> to determine whether or not it is currently computing something. |
||
=={{header|Nim}}== |
|||
We use Threads objects which are mapped to system threads. Access to buckets is protected by locks (one lock per bucket). We use also a lock to protect the random number generator which is not thread-safe. |
|||
The main thread sleeps during 10 seconds, then ask the threads to terminate. For this purpose, we could have used a simple boolean but we have rather chosen to send the termination message via a channel. So each thread receives the number of the channel to listen to and checks regularly if a message ask it to terminate. |
|||
<lang Nim>import locks |
|||
import math |
|||
import os |
|||
import random |
|||
const N = 10 # Number of buckets. |
|||
const MaxInit = 99 # Maximum initial value for buckets. |
|||
var buckets: array[1..N, Natural] # Array of buckets. |
|||
var bucketLocks: array[1..N, Lock] # Array of bucket locks. |
|||
var randomLock: Lock # Lock to protect the random number generator. |
|||
var terminate: array[3, Channel[bool]] # Used to ask threads to terminate. |
|||
#--------------------------------------------------------------------------------------------------- |
|||
proc getTwoIndexes(): tuple[a, b: int] = |
|||
## Get two indexes from the random number generator. |
|||
result.a = rand(1..N) |
|||
result.b = rand(2..N) |
|||
if result.b == result.a: result.b = 1 |
|||
#--------------------------------------------------------------------------------------------------- |
|||
proc equalize(num: int) {.thread.} = |
|||
## Try to equalize two buckets. |
|||
var b1, b2: int # Bucket indexes. |
|||
while true: |
|||
# Select the two buckets to "equalize". |
|||
withLock randomLock: |
|||
(b1, b2) = getTwoIndexes() |
|||
if b1 > b2: swap b1, b2 # We want "b1 < b2" to avoid deadlocks. |
|||
# Perform equalization. |
|||
withLock bucketLocks[b1]: |
|||
withLock bucketLocks[b2]: |
|||
let target = (buckets[b1] + buckets[b2]) div 2 |
|||
let delta = target - buckets[b1] |
|||
inc buckets[b1], delta |
|||
dec buckets[b2], delta |
|||
# Check termination. |
|||
let (available, stop) = tryRecv terminate[num] |
|||
if available and stop: break |
|||
#--------------------------------------------------------------------------------------------------- |
|||
proc distribute(num: int) {.thread.} = |
|||
## Redistribute contents of two buckets. |
|||
var b1, b2: int # Bucket indexes. |
|||
var factor: float # Ratio used to compute the new value for "b1". |
|||
while true: |
|||
# Select the two buckets for redistribution and the redistribution factor. |
|||
withLock randomLock: |
|||
(b1, b2) = getTwoIndexes() |
|||
factor = rand(0.0..1.0) |
|||
if b1 > b2: swap b1, b2 # We want "b1 < b2" to avoid deadlocks.. |
|||
# Perform redistribution. |
|||
withLock bucketLocks[b1]: |
|||
withLock bucketLocks[b2]: |
|||
let sum = buckets[b1] + buckets[b2] |
|||
let value = (sum.toFloat * factor).toInt |
|||
buckets[b1] = value |
|||
buckets[b2] = sum - value |
|||
# Check termination. |
|||
let (available, stop) = tryRecv terminate[num] |
|||
if available and stop: break |
|||
#--------------------------------------------------------------------------------------------------- |
|||
proc display(num: int) {.thread.} = |
|||
## Display the content of buckets and the sum (which should be constant). |
|||
while true: |
|||
for i in 1..N: acquire bucketLocks[i] |
|||
echo buckets, " Total = ", sum(buckets) |
|||
for i in countdown(N, 1): release bucketLocks[i] |
|||
os.sleep(1000) |
|||
# Check termination. |
|||
let (available, stop) = tryRecv terminate[num] |
|||
if available and stop: break |
|||
#——————————————————————————————————————————————————————————————————————————————————————————————————— |
|||
randomize() |
|||
# Initialize the buckets with a random value. |
|||
for bucket in buckets.mitems: |
|||
bucket = rand(1..MaxInit) |
|||
# Initialize the locks. |
|||
randomLock.initLock() |
|||
for lock in bucketLocks.mitems: |
|||
lock.initLock() |
|||
# Open the channels. |
|||
for c in terminate.mitems: |
|||
c.open() |
|||
# Create and launch the threads. |
|||
var tequal, tdist, tdisp: Thread[int] |
|||
tequal.createThread(equalize, 0) |
|||
tdist.createThread(distribute, 1) |
|||
tdisp.createThread(display, 2) |
|||
sleep(10000) |
|||
# Ask the threads to stop. |
|||
for c in terminate.mitems: |
|||
c.send(true) |
|||
joinThreads([tequal, tdist, tdisp]) |
|||
# Free resources. |
|||
randomLock.deinitLock() |
|||
for lock in bucketLocks.mitems: |
|||
lock.deinitLock() |
|||
for c in terminate.mitems: |
|||
c.close()</lang> |
|||
{{out}} |
|||
<pre>Total = 588 [92, 63, 33, 68, 66, 37, 26, 66, 77, 60] |
|||
Total = 588 [91, 3, 41, 126, 34, 3, 25, 92, 13, 160] |
|||
Total = 588 [129, 9, 80, 6, 68, 8, 73, 45, 69, 101] |
|||
Total = 588 [87, 71, 144, 20, 11, 54, 72, 48, 63, 18] |
|||
Total = 588 [158, 71, 110, 51, 19, 60, 27, 31, 10, 51] |
|||
Total = 588 [97, 43, 5, 70, 71, 104, 25, 17, 112, 44] |
|||
Total = 588 [68, 50, 12, 51, 128, 8, 21, 143, 53, 54] |
|||
Total = 588 [31, 47, 156, 81, 69, 5, 28, 76, 66, 29] |
|||
Total = 588 [97, 3, 27, 82, 42, 120, 72, 74, 39, 32] |
|||
Total = 588 [30, 39, 79, 109, 62, 62, 13, 14, 54, 126]</pre> |
|||
=={{header|Oz}}== |
=={{header|Oz}}== |