Atomic updates: Difference between revisions

m
(Added Oz solution.)
m (→‎{{header|Wren}}: Minor tidy)
 
(117 intermediate revisions by 56 users not shown)
Line 1:
{{task|Concurrency}}
{{requires|Concurrency}}
 
;Task:
Define a data type consisting of a fixed number of 'buckets', each containing a nonnegative integer value, which supports operations to
Define a data type consisting of a fixed number of 'buckets', each containing a nonnegative integer value, which supports operations to:
# get the current value of any bucket
# remove a specified amount from one specified bucket and add it to another, preserving the total of all bucket values, and [[wp:Clamping (graphics)|clamping]] the transferred amount to ensure the values remain nonnegativenon-negative
 
----
Line 12 ⟶ 14:
# At whatever rate is convenient, display (by any means) the total value and, optionally, the individual values of each bucket.
 
<br>
The display task need not be explicit; use of e.g. a debugger or trace tool is acceptable provided it is simple to set up to provide the display.
 
----
 
This task is intended as an exercise in ''atomic'' operations. &nbsp; The sum of the bucket values must be preserved even if the two tasks attempt to perform transfers simultaneously, and a straightforward solution is to ensure that at any time, only one transfer is actually occurring — that the transfer operation is ''atomic''.
<br><br>
 
=={{header|8th}}==
<syntaxhighlight lang="forth">var bucket
var bucket-size
 
\ The 'bucket' will be a simple array of some values:
: genbucket \ n --
a:new swap
(
\ make a random int up to 1000
rand-pcg n:abs 1000 n:mod
a:push
) swap times
bucket ! ;
 
\ display bucket and its total:
: .bucket
bucket lock @
dup . space
' n:+ 0 a:reduce . cr
bucket unlock drop ;
 
\ Get current value of bucket #x
: bucket@ \ n -- bucket[n]
bucket @
swap a:@ nip ;
 
\ Transfer x from bucket n to bucket m
: bucket-xfer \ m n x --
>r bucket @
\ m n bucket
over a:@ r@ n:-
rot swap a:!
\ m bucket
over a:@ r> n:+
rot swap a:!
drop ;
 
\ Get two random indices to check (ensure they're not the same):
: pick2
rand-pcg n:abs bucket-size @ n:mod dup >r
repeat
drop
rand-pcg n:abs bucket-size @ n:mod
r@ over n:=
while!
r> ;
 
\ Pick two buckets and make them more equal (by a quarter of their difference):
: make-equal
repeat
pick2
bucket lock @
third a:@ >r
over a:@ r> n:-
\ if they are equal, do nothing
dup not if
\ equal, so do nothing
drop -rot 2drop
else
4 n:/ n:int
>r -rot r>
bucket-xfer
then
drop
bucket unlock drop
again ;
 
\ Moves a quarter of the smaller value from one (random) bucket to another:
: make-redist
repeat
pick2 bucket lock @
\ n m bucket
over a:@ >r \ n m b b[m]
third a:@ r> \ n m b b[n]
n:min 4 n:/ n:int
nip bucket-xfer
 
bucket unlock drop
again ;
 
: app:main
\ create 10 buckets with random positive integer values:
10 genbucket bucket @ a:len bucket-size ! drop
 
\ print the bucket
.bucket
 
\ the problem's tasks:
' make-equal t:task
' make-redist t:task
 
\ the print-the-bucket task. We'll do it just 10 times and then quit:
( 1 sleep .bucket ) 10 times
bye ;</syntaxhighlight>
{{out}}<pre>[941,654,311,605,332,822,62,658,9,348] 4742
[289,98,710,698,183,490,675,688,793,118] 4742
[269,51,141,11,3,1284,1371,436,344,832] 4742
[1097,229,1097,307,421,25,85,676,188,617] 4742
[503,475,459,467,458,477,451,488,460,504] 4742
[480,498,484,460,481,464,467,488,481,439] 4742
[442,491,511,446,540,487,424,489,524,388] 4742
[3,306,114,88,185,366,2331,202,1138,9] 4742
[312,187,212,616,698,790,551,572,568,236] 4742
[473,474,475,474,474,473,476,475,473,475] 4742
[466,457,448,468,454,501,479,490,469,510] 4742
</pre>
 
=={{header|Ada}}==
<langsyntaxhighlight lang="ada">with Ada.Text_IO; use Ada.Text_IO;
with Ada.Numerics.Discrete_Random;
Line 103 ⟶ 214:
end;
end loop;
end Test_Updates;</langsyntaxhighlight>
The array of buckets is a protected object which controls access to its state. The task Equalize averages pairs of buckets. The task Mess_Up moves content of one bucket to another. The main task performs monitoring of the buckets state. Sample output:
<pre>
Line 118 ⟶ 229:
...
</pre>
 
=={{header|AutoHotkey}}==
<syntaxhighlight lang="autohotkey">Bucket := [], Buckets := 10, Originaltotal = 0
loop, %Buckets% {
Random, rnd, 0,50
Bucket[A_Index] := rnd, Originaltotal += rnd
}
 
loop 100
{
total := 0
Randomize(B1, B2, Buckets)
temp := (Bucket[B1] + Bucket[B2]) /2
Bucket[B1] := floor(temp), Bucket[B2] := Ceil(temp) ; values closer to equal
 
Randomize(B1, B2, Buckets)
temp := Bucket[B1] + Bucket[B2]
Random, value, 0, %temp%
Bucket[B1] := value, Bucket[B2] := temp-value ; redistribute values arbitrarily
VisualTip := "Original Total = " Originaltotal "`n"
loop, %Buckets%
VisualTip .= SubStr("0" Bucket[A_Index], -1) " : " x(Bucket[A_Index]) "`n" , total += Bucket[A_Index]
ToolTip % VisualTip "Current Total = " total
if (total <> Originaltotal)
MsgBox "Error"
Sleep, 100
}
return
 
Randomize(ByRef B1, ByRef B2, Buckets){
Random, B1, 1, %Buckets%
Loop
Random, B2, 1, %Buckets%
until (B1<>B2)
}
 
x(n){
loop, % n
Res.= ">"
return Res
}</syntaxhighlight>
 
=={{header|BBC BASIC}}==
{{works with|BBC BASIC for Windows}}
The BBC BASIC interpreter is single-threaded so the 'concurrent' tasks are implemented by timer events. In this context an 'atomic' update means one which takes place within a single BASIC statement, so it cannot be 'interrupted'. Two (or more) buckets can be updated atomically by making them RETURN parameters of a procedure.
<syntaxhighlight lang="bbcbasic"> INSTALL @lib$+"TIMERLIB"
DIM Buckets%(100)
FOR i% = 1 TO 100 : Buckets%(i%) = RND(10) : NEXT
tid0% = FN_ontimer(10, PROCdisplay, 1)
tid1% = FN_ontimer(11, PROCflatten, 1)
tid2% = FN_ontimer(12, PROCroughen, 1)
ON ERROR PROCcleanup : REPORT : PRINT : END
ON CLOSE PROCcleanup : QUIT
REPEAT
WAIT 0
UNTIL FALSE
END
DEF PROCdisplay
PRINT SUM(Buckets%()) " ", MOD(Buckets%())
ENDPROC
DEF PROCflatten
LOCAL d%, i%, j%
REPEAT
i% = RND(100)
j% = RND(100)
UNTIL i%<>j%
d% = Buckets%(i%) - Buckets%(j%)
PROCatomicupdate(Buckets%(i%), Buckets%(j%), d% DIV 4)
ENDPROC
DEF PROCroughen
LOCAL i%, j%
REPEAT
i% = RND(100)
j% = RND(100)
UNTIL i%<>j%
PROCatomicupdate(Buckets%(i%), Buckets%(j%), RND(10))
ENDPROC
DEF PROCatomicupdate(RETURN src%, RETURN dst%, amt%)
IF amt% > src% amt% = src%
IF amt% < -dst% amt% = -dst%
src% -= amt%
dst% += amt%
ENDPROC
DEF PROCcleanup
PROC_killtimer(tid0%)
PROC_killtimer(tid1%)
PROC_killtimer(tid2%)
ENDPROC</syntaxhighlight>
 
=={{header|C}}==
Line 125 ⟶ 335:
 
{{libheader|pthread}}
<langsyntaxhighlight lang="c">#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
Line 235 ⟶ 445:
for(i=0; i < N_BUCKETS; i++) pthread_mutex_destroy(bucket_mutex+i);
return EXIT_SUCCESS;
}</langsyntaxhighlight>
 
===With OpenMP===
Compiled with <code>gcc -std=c99 -fopenmp</code>. The <code>#pragma omp critical</code> ensures the following block is entered by one thread at a time.
<syntaxhighlight lang="c">#include <stdio.h>
#include <stdlib.h>
#include <omp.h>
 
#define irand(n) (n * (double)rand()/(RAND_MAX + 1.0))
 
int bucket[10];
int main()
{
int i;
for (i = 0; i < 10; i++) bucket[i] = 1000;
omp_set_num_threads(3);
 
#pragma omp parallel private(i)
for (i = 0; i < 10000; i++) {
int from, to, mode, diff = 0, sum;
 
from = irand(10);
do { to = irand(10); } while (from == to);
mode = irand(10);
 
switch (mode) {
case 0:
case 1:
case 2: /* equalize */
diff = (bucket[from] - bucket[to]) / 2;
break;
 
case 3: /* report */
sum = 0;
for (int j = 0; j < 10; j++) {
printf("%d ", bucket[j]);
sum += bucket[j];
}
printf(" Sum: %d\n", sum);
continue;
 
default: /* random transfer */
diff = irand(bucket[from]);
break;
}
 
#pragma omp critical
{
bucket[from] -= diff;
bucket[to] += diff;
}
}
 
return 0;
}</syntaxhighlight>Output:<syntaxhighlight lang="text">1000 1000 1000 1798 1000 1000 1000 1000 202 1000 Sum: 10000
595 800 2508 2750 470 1209 283 314 601 470 Sum: 10000
5 521 3339 1656 351 1038 1656 54 508 872 Sum: 10000
.
.
.
752 490 385 2118 1503 508 384 509 1110 2241 Sum: 10000
752 823 385 2118 1544 508 10 509 1110 2241 Sum: 10000</syntaxhighlight>
 
=={{header|C sharp|C#}}==
This C# implementation uses a class to hold the buckets and data associated with them. The ThreadSafeBuckets class implements thread-stability, and ensures that two threads cannot operate on the same data at the same time. Additionally, the class uses a seperate mutex for each bucket, allowing multiple operations to occur at once if they do not alter the same buckets.
 
I updated the original class for a few things:
<lang csharp>using System; //Rand class
- Changed to using object locks and Montor.Enter rather than Mutexes. This allows use of the cleaner "lock" statement, and also has lower runtime overhead for in process locks
- The previous implementation tracked a "swapped" state - which seems a harder way to tackle the problem. You need to acquire the locks in the correct order, not swap i and j
 
<syntaxhighlight lang="csharp">
using System; //Rand class
using System.Threading; //Thread, Mutex classes
public class ThreadSafeBuckets
Line 248 ⟶ 524:
Random rand = new Random();
int[] Buckets;
Mutexobject[] Mutexeslocks; //Mutexes for each bucket so they can lock individually
public int BucketCount { get; private set; }
public ThreadSafeBuckets(int bucketcount)
Line 255 ⟶ 531:
BucketCount = bucketcount;
Buckets = new int[bucketcount];
Mutexeslocks = new Mutexobject[bucketcount];
int startingtotal = 0;
for (int i = 0; i < BucketCount; i++)
{
Mutexeslocks[i] = new Mutexobject(false);
Buckets[i] = rand.Next(30);
startingtotal += Buckets[i];
Line 273 ⟶ 549:
{
//Transfer amount from bucket i to bucket j
bool swapped = false;
if (i > BucketCount || j > BucketCount || i < 0 || j < 0 ||
i == j || amount < 0)
return;
 
if (i > j)
{ //To prevent deadlock, always try to lock the lower bucket first
lock (locks[Math.Min(i, int temp1 = i;j)])
ilock =(locks[Math.Max(i, j;)])
j = temp1;{
swapped = true; //Make sure we stilldon't transfer out more than is in the right directionbucket
amount = -Math.Min(amount, Buckets[i]);
 
}
//GetDo the lockstransfer
Mutexes Buckets[i].WaitOne() -= amount;
Mutexes Buckets[j].WaitOne() += amount;
}
//Make sure don't transfer out more than what's in the bucket
if (amount > Buckets[i] && !swapped)
amount = Buckets[i];
if (-amount > Buckets[j] && swapped)
amount = -Buckets[j];
//Do the transfer
Buckets[i] -= amount;
Buckets[j] += amount;
//Release the locks
Mutexes[i].ReleaseMutex();
Mutexes[j].ReleaseMutex();
}
 
public void PrintBuckets()
{
Line 306 ⟶ 572:
for (int i = 0; i < BucketCount; i++)
{
MutexesMonitor.Enter(locks[i].WaitOne();
Console.Write(Buckets[i] + " ");
counter += Buckets[i];
Line 313 ⟶ 579:
Console.Write("= " + counter);
Console.WriteLine();
 
for (int i = 0; i < BucketCount; i++)
{foreach (var l in locks)
Mutexes[i]Monitor.ReleaseMutexExit(l);
}
}
}
Line 375 ⟶ 640:
}
}
}</langsyntaxhighlight>
 
Sample Output:
Line 387 ⟶ 652:
11 17 19 1 18 1 12 35 26 16 = 156
</pre>
 
=={{header|C++}}==
{{trans|C}}
 
{{works with|C++11}}
<syntaxhighlight lang="cpp">#include <algorithm>
#include <array>
#include <chrono>
#include <iomanip>
#include <iostream>
#include <mutex>
#include <random>
#include <thread>
 
using namespace std;
 
constexpr int bucket_count = 15;
 
void equalizer(array<int, bucket_count>& buckets,
array<mutex, bucket_count>& bucket_mutex) {
random_device rd;
mt19937 gen(rd());
uniform_int_distribution<> dist_bucket(0, bucket_count - 1);
 
while (true) {
int from = dist_bucket(gen);
int to = dist_bucket(gen);
if (from != to) {
lock_guard<mutex> lock_first(bucket_mutex[min(from, to)]);
lock_guard<mutex> lock_second(bucket_mutex[max(from, to)]);
int diff = buckets[from] - buckets[to];
int amount = abs(diff / 2);
if (diff < 0) {
swap(from, to);
}
buckets[from] -= amount;
buckets[to] += amount;
}
}
}
 
void randomizer(array<int, bucket_count>& buckets,
array<mutex, bucket_count>& bucket_mutex) {
random_device rd;
mt19937 gen(rd());
uniform_int_distribution<> dist_bucket(0, bucket_count - 1);
 
while (true) {
int from = dist_bucket(gen);
int to = dist_bucket(gen);
if (from != to) {
lock_guard<mutex> lock_first(bucket_mutex[min(from, to)]);
lock_guard<mutex> lock_second(bucket_mutex[max(from, to)]);
uniform_int_distribution<> dist_amount(0, buckets[from]);
int amount = dist_amount(gen);
buckets[from] -= amount;
buckets[to] += amount;
}
}
}
 
void print_buckets(const array<int, bucket_count>& buckets) {
int total = 0;
for (const int& bucket : buckets) {
total += bucket;
cout << setw(3) << bucket << ' ';
}
cout << "= " << setw(3) << total << endl;
}
 
int main() {
random_device rd;
mt19937 gen(rd());
uniform_int_distribution<> dist(0, 99);
 
array<int, bucket_count> buckets;
array<mutex, bucket_count> bucket_mutex;
for (int& bucket : buckets) {
bucket = dist(gen);
}
print_buckets(buckets);
 
thread t_eq(equalizer, ref(buckets), ref(bucket_mutex));
thread t_rd(randomizer, ref(buckets), ref(bucket_mutex));
 
while (true) {
this_thread::sleep_for(chrono::seconds(1));
for (mutex& mutex : bucket_mutex) {
mutex.lock();
}
print_buckets(buckets);
for (mutex& mutex : bucket_mutex) {
mutex.unlock();
}
}
return 0;
}</syntaxhighlight>
 
=={{header|Clojure}}==
Function returning a new map containing altered values:
<langsyntaxhighlight lang="lisp">(defn xfer [m from to amt]
(let [{f-bal from t-bal to} m
f-bal (- f-bal amt)
Line 396 ⟶ 758:
(if (or (neg? f-bal) (neg? t-bal))
(throw (IllegalArgumentException. "Call results in negative balance."))
(assoc m from f-bal to t-bal))))</langsyntaxhighlight>
Since clojure data structures are immutable, atomic mutability occurs via a reference, in this case an atom:
<langsyntaxhighlight lang="lisp">(def *data* (atom {:a 100 :b 100})) ;; *data* is an atom holding a map
(swap! *data* xfer :a :b 50) ;; atomically results in *data* holding {:a 50 :b 150}</langsyntaxhighlight>
Now for the test:
<langsyntaxhighlight lang="lisp">(defn equalize [m a b]
(let [{a-val a b-val b} m
diff (- a-val b-val)
Line 422 ⟶ 784:
 
(.start thread-eq)
(.start thread-rand)</langsyntaxhighlight>
 
=={{header|Common Lisp}}==
Depends on libraries in Quicklisp. STMX is a library that provides Software Transactional Memory.
<syntaxhighlight lang="lisp">(ql:quickload '(:alexandria :stmx :bordeaux-threads))
 
(defpackage :atomic-updates
(:use :cl))
 
(in-package :atomic-updates)
 
(defvar *buckets* nil)
(defvar *running* nil)
 
(defun distribute (ratio a b)
"Atomically redistribute the values of buckets A and B by RATIO."
(stmx:atomic
(let* ((sum (+ (stmx:$ a) (stmx:$ b)))
(a2 (truncate (* ratio sum))))
(setf (stmx:$ a) a2)
(setf (stmx:$ b) (- sum a2)))))
 
(defun runner (ratio-func)
"Continously distribute to two different elements in *BUCKETS* with the
value returned from RATIO-FUNC."
(loop while *running*
do (let ((a (alexandria:random-elt *buckets*))
(b (alexandria:random-elt *buckets*)))
(unless (eq a b)
(distribute (funcall ratio-func) a b)))))
 
(defun print-buckets ()
"Atomically get the bucket values and print out their metrics."
(let ((buckets (stmx:atomic (map 'vector 'stmx:$ *buckets*))))
(format t "Buckets: ~a~%Sum: ~a~%" buckets (reduce '+ buckets))))
 
(defun scenario ()
(setf *buckets* (coerce (loop repeat 20 collect (stmx:tvar 10)) 'vector))
(setf *running* t)
(bt:make-thread (lambda () (runner (constantly 0.5))))
(bt:make-thread (lambda () (runner (lambda () (random 1.0))))))</syntaxhighlight>
{{out}}
<syntaxhighlight lang="lisp">ATOMIC-UPDATES> (scenario)
#<SB-THREAD:THREAD "Anonymous thread" RUNNING {10058441D3}>
ATOMIC-UPDATES> (loop repeat 3 do (print-buckets) (sleep 1))
Buckets: #(8 4 12 17 12 10 5 10 9 10 4 11 4 15 16 20 11 8 4 10)
Sum: 200
Buckets: #(2 12 24 7 8 3 13 6 8 31 0 9 7 11 12 8 8 12 15 4)
Sum: 200
Buckets: #(1 2 3 3 2 8 33 23 0 8 4 11 24 2 3 5 32 8 2 26)
Sum: 200
NIL</syntaxhighlight>
 
=={{header|D}}==
This implements a more scalable version than most of the other languages, by using a lock per bucket instead of a single lock for the whole array.
 
<syntaxhighlight lang="d">import std.stdio: writeln;
import std.conv: text;
import std.random: uniform, Xorshift;
import std.algorithm: min, max;
import std.parallelism: task;
import core.thread: Thread;
import core.sync.mutex: Mutex;
import core.time: seconds;
 
__gshared uint transfersCount;
 
final class Buckets(size_t nBuckets) if (nBuckets > 0) {
alias TBucketValue = uint;
 
// The trailing padding avoids cache line contention
// when run with two or more cores.
align(128) private static struct Bucket {
TBucketValue value;
Mutex mtx;
alias value this;
}
 
private Bucket[nBuckets] buckets;
private bool running;
 
public this() {
this.running = true;
foreach (ref b; buckets)
b = Bucket(uniform(0, 100), new Mutex);
}
 
public TBucketValue opIndex(in size_t index) const pure nothrow {
return buckets[index];
}
 
public void transfer(in size_t from, in size_t to,
in TBucketValue amount) {
immutable low = min(from, to);
immutable high = max(from, to);
buckets[low].mtx.lock();
buckets[high].mtx.lock();
 
scope(exit) {
buckets[low].mtx.unlock();
buckets[high].mtx.unlock();
}
 
immutable realAmount = min(buckets[from].value, amount);
buckets[from] -= realAmount;
buckets[to ] += realAmount;
transfersCount++;
}
 
@property size_t length() const pure nothrow {
return this.buckets.length;
}
 
void toString(in void delegate(const(char)[]) sink) {
TBucketValue total = 0;
foreach (ref b; buckets) {
b.mtx.lock();
total += b;
}
 
scope(exit)
foreach (ref b; buckets)
b.mtx.unlock();
 
sink(text(buckets));
sink(" ");
sink(text(total));
}
}
 
void randomize(size_t N)(Buckets!N data) {
auto rng = Xorshift(1);
 
while (data.running) {
immutable i = uniform(0, data.length, rng);
immutable j = uniform(0, data.length, rng);
immutable amount = uniform!"[]"(0, 20, rng);
data.transfer(i, j, amount);
}
}
 
void equalize(size_t N)(Buckets!N data) {
auto rng = Xorshift(1);
 
while (data.running) {
immutable i = uniform(0, data.length, rng);
immutable j = uniform(0, data.length, rng);
immutable a = data[i];
immutable b = data[j];
if (a > b)
data.transfer(i, j, (a - b) / 2);
else
data.transfer(j, i, (b - a) / 2);
}
}
 
void display(size_t N)(Buckets!N data) {
foreach (immutable _; 0 .. 10) {
writeln(transfersCount, " ", data);
transfersCount = 0;
Thread.sleep(1.seconds);
}
data.running = false;
}
 
void main() {
writeln("N. transfers, buckets, buckets sum:");
auto data = new Buckets!20();
task!randomize(data).executeInNewThread();
task!equalize(data).executeInNewThread();
task!display(data).executeInNewThread();
}</syntaxhighlight>
{{out}}
<pre>N. transfers, buckets, buckets sum:
445977 [0, 175, 33, 18, 26, 61, 34, 13, 181, 8, 28, 12, 28, 47, 4, 12, 3, 76, 46, 59] 864
4863591 [32, 18, 45, 12, 69, 29, 98, 64, 108, 28, 54, 16, 15, 93, 56, 0, 4, 16, 48, 59] 864
4872790 [46, 162, 6, 2, 42, 70, 77, 34, 78, 99, 19, 0, 10, 59, 61, 13, 0, 27, 0, 59] 864
5102493 [1, 10, 120, 159, 108, 0, 51, 0, 35, 74, 0, 7, 14, 5, 6, 23, 53, 99, 40, 59] 864
5139426 [42, 43, 42, 42, 42, 42, 43, 42, 42, 42, 42, 43, 43, 42, 42, 43, 43, 43, 42, 59] 864
4853088 [12, 108, 18, 53, 25, 62, 37, 86, 141, 0, 45, 18, 0, 30, 0, 129, 11, 0, 30, 59] 864
4739723 [84, 12, 105, 80, 140, 0, 6, 53, 17, 86, 55, 0, 0, 41, 14, 51, 25, 11, 25, 59] 864
5295588 [43, 43, 42, 42, 57, 53, 43, 34, 42, 66, 61, 49, 10, 39, 29, 24, 48, 50, 30, 59] 864
5137883 [42, 43, 42, 42, 43, 43, 43, 42, 42, 42, 43, 42, 42, 43, 42, 43, 49, 42, 35, 59] 864
5143735 [42, 42, 43, 43, 43, 42, 43, 42, 42, 43, 42, 43, 42, 42, 42, 42, 42, 43, 42, 59] 864</pre>
 
=={{header|E}}==
Line 436 ⟶ 981:
This example uses a Java AWT window to display the current state of the buckets.
 
<langsyntaxhighlight lang="e">#!/usr/bin/env rune
pragma.syntax("0.9")
 
Line 558 ⟶ 1,103:
 
frame.show()
interp.waitAtTop(done)</langsyntaxhighlight>
 
=={{header|F#Erlang}}==
Erlang has a built in database (Mnesia) with atomic operations. This is another way.
Instead of deleting the Buckets process manually I use spawn_link(). That way Buckets goes away with the user. Output is:
<pre>
[1,2,3,4,5,6,7,8,9,10] = 55
[1,3,4,7,2,5,13,8,4,8] = 55
[6,13,6,0,8,3,1,0,8,10] = 55
[8,0,9,0,5,9,8,8,8,0] = 55
[8,11,9,3,1,12,8,0,0,3] = 55
[13,4,3,8,1,5,10,4,5,2] = 55
[6,6,9,5,6,5,6,1,5,6] = 55
[20,7,5,0,5,0,0,10,8,0] = 55
[2,10,0,10,0,4,8,3,15,3] = 55
[0,11,7,0,4,16,7,0,10,0] = 55
</pre>
<syntaxhighlight lang="erlang">
-module( atomic_updates ).
-export( [buckets/1, buckets_get/2, buckets_get_all/1, buckets_move_contents/4, task/0] ).
 
buckets( N ) ->
Buckets = erlang:list_to_tuple( lists:seq(1, N) ),
erlang:spawn_link( fun() -> buckets_loop(Buckets) end ).
 
buckets_get( N, Buckets_pid ) ->
{is_buckets_alive, true} = {is_buckets_alive, erlang:is_process_alive( Buckets_pid )},
Buckets_pid ! {get, N, erlang:self()},
receive
{value, Buckets_pid, Value} -> Value
end.
 
buckets_get_all( Buckets_pid ) ->
{is_buckets_alive, true} = {is_buckets_alive, erlang:is_process_alive( Buckets_pid )},
Buckets_pid ! {get_all, erlang:self()},
receive
{values, Buckets_pid, Values} -> Values
end.
 
buckets_move_contents( Amount, From, To, Buckets_pid ) ->
{is_buckets_alive, true} = {is_buckets_alive, erlang:is_process_alive( Buckets_pid )},
Buckets_pid ! {move_contents, Amount, From, To, erlang:self()},
receive
{move_contents_done, Buckets_pid} -> ok
end.
 
task() ->
erlang:spawn( fun() ->
N = 10,
Buckets = buckets( N ),
erlang:spawn_link( fun() -> closer_loop(N, Buckets) end ),
erlang:spawn_link( fun() -> redistribute_loop(N, Buckets) end ),
display_loop( 0, N, Buckets ),
erlang:exit( stop )
end ).
 
 
 
closer_loop( N, Buckets ) ->
One = random:uniform( N ),
Two = random:uniform( N ),
Difference = buckets_get( One, Buckets ) - buckets_get( Two, Buckets ),
{Amount, From, To} = closer_loop_how_to_move( Difference, One, Two ),
buckets_move_contents( Amount, From, To, Buckets ),
closer_loop( N, Buckets ).
 
closer_loop_how_to_move( Difference, One, Two ) when Difference < 0 ->
{-1* Difference div 2, Two, One};
closer_loop_how_to_move( Difference, One, Two ) ->
{Difference div 2, One, Two}.
 
buckets_loop( Buckets ) ->
receive
{get, N, Pid} ->
Pid ! {value, erlang:self(), erlang:element( N, Buckets )},
buckets_loop( Buckets );
{get_all, Pid} ->
Pid ! {values, erlang:self(), erlang:tuple_to_list( Buckets )},
buckets_loop( Buckets );
{move_contents, Amount, From, To, Pid} ->
Pid ! {move_contents_done, erlang:self()},
buckets_loop( buckets_loop_move_contents(Amount, From, To, Buckets) )
end.
 
buckets_loop_move_contents( _Amount, Same, Same, Buckets ) ->
Buckets;
buckets_loop_move_contents( Amount, From, To, Buckets ) ->
Amount_from = erlang:element( From, Buckets ),
Clamped_amount = erlang:min( Amount, Amount_from ),
Removed = erlang:setelement( From, Buckets, Amount_from - Clamped_amount ),
Amount_to = erlang:element( To, Buckets ) + Clamped_amount,
erlang:setelement( To, Removed, Amount_to ).
 
display_loop( N, N, _Buckets ) -> ok;
display_loop( Counter, N, Buckets ) ->
Contents = buckets_get_all( Buckets ),
io:fwrite( "~p = ~p~n", [Contents, lists:sum(Contents)] ),
timer:sleep( 100 ),
display_loop( Counter + 1, N, Buckets ).
 
redistribute_loop( N, Buckets ) ->
Amount = random:uniform( N ),
From = random:uniform( N ),
To = random:uniform( N ),
buckets_move_contents( Amount, From, To, Buckets ),
redistribute_loop( N, Buckets ).
</syntaxhighlight>
 
=={{header|Euphoria}}==
<syntaxhighlight lang="euphoria">function move(sequence s, integer amount, integer src, integer dest)
if src < 1 or src > length(s) or dest < 1 or dest > length(s) or amount < 0 then
return -1
else
if src != dest and amount then
if amount > s[src] then
amount = s[src]
end if
s[src] -= amount
s[dest] += amount
end if
return s
end if
end function
 
sequence buckets
buckets = repeat(100,10)
 
procedure equalize()
integer i, j, diff
while 1 do
i = rand(length(buckets))
j = rand(length(buckets))
diff = buckets[i] - buckets[j]
if diff >= 2 then
buckets = move(buckets, floor(diff / 2), i, j)
elsif diff <= -2 then
buckets = move(buckets, -floor(diff / 2), j, i)
end if
task_yield()
end while
end procedure
 
procedure redistribute()
integer i, j
while 1 do
i = rand(length(buckets))
j = rand(length(buckets))
if buckets[i] then
buckets = move(buckets, rand(buckets[i]), i, j)
end if
task_yield()
end while
end procedure
 
function sum(sequence s)
integer sum
sum = 0
for i = 1 to length(s) do
sum += s[i]
end for
return sum
end function
 
atom task
 
task = task_create(routine_id("equalize"), {})
task_schedule(task, 1)
 
task = task_create(routine_id("redistribute"), {})
task_schedule(task, 1)
 
task_schedule(0, {0.5, 0.5})
 
for i = 1 to 24 do
print(1,buckets)
printf(1," sum: %d\n", {sum(buckets)})
task_yield()
end for</syntaxhighlight>
 
Output:
<pre>{100,100,100,100,100,100,100,100,100,100} sum: 1000
{150,77,68,150,113,126,14,192,68,42} sum: 1000
{46,64,58,117,139,59,143,114,130,130} sum: 1000
{82,99,13,99,58,117,10,191,194,137} sum: 1000
{72,65,68,193,67,65,112,106,128,124} sum: 1000
{43,43,42,31,234,104,105,234,30,134} sum: 1000
{83,106,31,82,174,62,254,71,106,31} sum: 1000
{145,102,247,86,159,30,87,35,102,7} sum: 1000
{93,102,114,40,126,48,243,101,10,123} sum: 1000
{160,38,9,89,182,240,116,15,61,90} sum: 1000
{31,45,123,31,308,189,71,0,79,123} sum: 1000
{9,86,198,87,72,194,168,148,38,0} sum: 1000
{122,99,42,99,140,128,106,68,155,41} sum: 1000
{223,45,0,220,220,50,153,6,82,1} sum: 1000
{171,68,192,100,78,31,100,0,31,229} sum: 1000
{47,70,108,253,66,113,70,92,157,24} sum: 1000
{113,85,147,84,97,21,93,180,99,81} sum: 1000
{82,35,8,75,166,342,48,79,99,66} sum: 1000
{65,53,71,36,72,108,127,146,116,206} sum: 1000
{154,15,107,47,50,204,82,177,107,57} sum: 1000
{63,127,62,126,261,57,127,95,70,12} sum: 1000
{25,50,0,39,55,105,586,54,47,39} sum: 1000
{31,86,137,66,117,116,157,121,110,59} sum: 1000
{129,65,27,38,135,54,175,129,135,113} sum: 1000
</pre>
 
=={{header|F Sharp|F#}}==
 
The Buckets class is thread safe and its private higher-order Lock function ensures that locks are taken out in order (to avoid deadlocks):
 
<langsyntaxhighlight lang="fsharp">
open System.Threading
 
Line 629 ⟶ 1,378:
 
do
letuse bucket = new Buckets(10)
let equalizer = Thread(equalizer bucket)
let randomizer = Thread(randomizer bucket)
Line 638 ⟶ 1,387:
Thread.Sleep 100
bucket.Print()
</syntaxhighlight>
</lang>
 
This program performs a million concurrent transfers. Typical output is:
 
<langsyntaxhighlight lang="fsharp">
[|100; 100; 100; 100; 100; 100; 100; 100; 100; 100|] = 1000
[|119; 61; 138; 115; 157; 54; 82; 58; 157; 59|] = 1000
Line 701 ⟶ 1,450:
[|208; 147; 18; 25; 178; 159; 23; 170; 36; 36|] = 1000
Press any key to continue . . .
</syntaxhighlight>
</lang>
 
=={{header|FreeBASIC}}==
{{trans|Run Basic}}
<syntaxhighlight lang="freebasic">Randomize Timer
Dim Shared As Uinteger cubo(1 To 10), a, i
For i As Uinteger = 1 To 10
cubo(i) = Int(Rnd * 90)
Next i
 
Function Display(cadena As String) As Uinteger
Dim As Uinteger valor
Print cadena; Spc(2);
For i As Uinteger = 1 To 10
valor += cubo(i)
Print Using "###"; cubo(i);
Next i
Print " Total:"; valor
Return valor
End Function
 
Sub Flatten(f As Uinteger)
Dim As Uinteger f1 = Int((f / 10) + .5), f2
For i As Uinteger = 1 To 10
cubo(i) = f1
f2 += f1
Next i
cubo(10) += f - f2
End Sub
 
Sub Transfer(a1 As Uinteger, a2 As Uinteger)
Dim As Uinteger temp = Int(Rnd * cubo(a1))
cubo(a1) -= temp
cubo(a2) += temp
End Sub
 
a = Display(" Display:") ' show original array
Flatten(a) ' flatten the array
a = Display(" Flatten:") ' show flattened array
Transfer(3, 5) ' transfer some amount from 3 to 5
Display(" 19 from 3 to 5:") ' show transfer array
Sleep</syntaxhighlight>
{{out}}
<pre> Display: 8 77 51 38 76 47 43 16 1 1 Total: 358
Flatten: 36 36 36 36 36 36 36 36 36 34 Total: 358
19 from 3 to 5: 36 36 21 36 51 36 36 36 36 34 Total: 358</pre>
 
=={{header|FutureBasic}}==
<syntaxhighlight lang="futurebasic">
local fn PopulateArrayWithRandomNumbers
NSUInteger i
for i = 0 to 9
mda (i) = rnd(90)
next
end fn
 
local fn Display( title as CFStringRef ) as NSUInteger
NSUInteger i, worth = 0
CFStringRef comma = @","
printf @"%@ [\b", title
for i = 0 to 9
worth += mda_integer (i)
if i == 9 then comma = @""
printf @"%2lu%@\b", mda_integer (i), comma
next
printf @"] Sum = %lu", worth
end fn = worth
 
local fn Flatten( f as NSUInteger )
NSUInteger i, f1 = int((f / 10) + .5 ), f2 = 0, temp
for i = 0 to 9
mda (i) = f1
f2 += f1
next
temp = mda_integer (9)
mda (9) = temp + f - f2
end fn
 
local fn Transfer( a1 as NSUInteger, a2 as NSUInteger )
NSUInteger t, temp = int( rnd( mda_integer ( a1 ) ) )
t = mda_integer ( a1 ) : mda ( a1 ) = t -temp
t = mda_integer ( a2 ) : mda ( a2 ) = t +temp
end fn
 
NSUInteger a, i
 
random
fn PopulateArrayWithRandomNumbers
a = fn Display( @" Initial array:" )
fn Flatten( a )
a = fn Display( @" Current values:" )
fn Transfer( 3, 5 )
fn Display( @" 19 from 3 to 5:" )
 
HandleEvents
</syntaxhighlight>
{{output}}
<pre>
Initial array: [28,73,90, 1,75,51,69,35,70,28] Sum = 520
Current values: [52,52,52,52,52,52,52,52,52,52] Sum = 520
19 from 3 to 5: [52,52,52,34,52,70,52,52,52,52] Sum = 520
</pre>
 
=={{header|Go}}==
<syntaxhighlight lang="go">package main
 
import (
"fmt"
"math/rand"
"sync"
"time"
)
 
const nBuckets = 10
 
type bucketList struct {
b [nBuckets]int // bucket data specified by task
 
// transfer counts for each updater, not strictly required by task but
// useful to show that the two updaters get fair chances to run.
tc [2]int
 
sync.Mutex // synchronization
}
 
// Updater ids, to track number of transfers by updater.
// these can index bucketlist.tc for example.
const (
idOrder = iota
idChaos
)
 
const initialSum = 1000 // sum of all bucket values
 
// Constructor.
func newBucketList() *bucketList {
var bl bucketList
// Distribute initialSum across buckets.
for i, dist := nBuckets, initialSum; i > 0; {
v := dist / i
i--
bl.b[i] = v
dist -= v
}
return &bl
}
 
// method 1 required by task, get current value of a bucket
func (bl *bucketList) bucketValue(b int) int {
bl.Lock() // lock before accessing data
r := bl.b[b]
bl.Unlock()
return r
}
 
// method 2 required by task
func (bl *bucketList) transfer(b1, b2, a int, ux int) {
// Get access.
bl.Lock()
// Clamping maintains invariant that bucket values remain nonnegative.
if a > bl.b[b1] {
a = bl.b[b1]
}
// Transfer.
bl.b[b1] -= a
bl.b[b2] += a
bl.tc[ux]++ // increment transfer count
bl.Unlock()
}
 
// additional useful method
func (bl *bucketList) snapshot(s *[nBuckets]int, tc *[2]int) {
bl.Lock()
*s = bl.b
*tc = bl.tc
bl.tc = [2]int{} // clear transfer counts
bl.Unlock()
}
 
var bl = newBucketList()
 
func main() {
// Three concurrent tasks.
go order() // make values closer to equal
go chaos() // arbitrarily redistribute values
buddha() // display total value and individual values of each bucket
}
 
// The concurrent tasks exercise the data operations by calling bucketList
// methods. The bucketList methods are "threadsafe", by which we really mean
// goroutine-safe. The conconcurrent tasks then do no explicit synchronization
// and are not responsible for maintaining invariants.
 
// Exercise 1 required by task: make values more equal.
func order() {
r := rand.New(rand.NewSource(time.Now().UnixNano()))
for {
b1 := r.Intn(nBuckets)
b2 := r.Intn(nBuckets - 1)
if b2 >= b1 {
b2++
}
v1 := bl.bucketValue(b1)
v2 := bl.bucketValue(b2)
if v1 > v2 {
bl.transfer(b1, b2, (v1-v2)/2, idOrder)
} else {
bl.transfer(b2, b1, (v2-v1)/2, idOrder)
}
}
}
 
// Exercise 2 required by task: redistribute values.
func chaos() {
r := rand.New(rand.NewSource(time.Now().Unix()))
for {
b1 := r.Intn(nBuckets)
b2 := r.Intn(nBuckets - 1)
if b2 >= b1 {
b2++
}
bl.transfer(b1, b2, r.Intn(bl.bucketValue(b1)+1), idChaos)
}
}
 
// Exercise 3 requred by task: display total.
func buddha() {
var s [nBuckets]int
var tc [2]int
var total, nTicks int
 
fmt.Println("sum ---updates--- mean buckets")
tr := time.Tick(time.Second / 10)
for {
<-tr
bl.snapshot(&s, &tc)
var sum int
for _, l := range s {
if l < 0 {
panic("sob") // invariant not preserved
}
sum += l
}
// Output number of updates per tick and cummulative mean
// updates per tick to demonstrate "as often as possible"
// of task exercises 1 and 2.
total += tc[0] + tc[1]
nTicks++
fmt.Printf("%d %6d %6d %7d %3d\n", sum, tc[0], tc[1], total/nTicks, s)
if sum != initialSum {
panic("weep") // invariant not preserved
}
}
}</syntaxhighlight>
{{out}}
<pre>
sum ---updates--- mean buckets
1000 317832 137235 455067 [100 100 100 100 100 100 100 100 100 100]
1000 391239 339389 592847 [ 85 266 81 85 131 37 62 80 111 62]
1000 509436 497362 730831 [ 70 194 194 62 16 193 10 16 126 119]
1000 512065 499038 800899 [100 100 100 100 100 100 100 100 100 100]
1000 250590 121947 715226 [ 47 271 78 61 34 199 73 58 100 79]
...
</pre>
 
=={{header|Groovy}}==
Solution:
<syntaxhighlight lang="groovy">class Buckets {
 
def cells = []
final n
 
Buckets(n, limit=1000, random=new Random()) {
this.n = n
(0..<n).each {
cells << random.nextInt(limit)
}
}
synchronized getAt(i) {
cells[i]
}
synchronized transfer(from, to, amount) {
assert from in (0..<n) && to in (0..<n)
def cappedAmt = [cells[from], amount].min()
cells[from] -= cappedAmt
cells[to] += cappedAmt
}
synchronized String toString() { cells.toString() }
}
 
def random = new Random()
 
def buckets = new Buckets(5)
 
def makeCloser = { i, j ->
synchronized(buckets) {
def targetDiff = (buckets[i]-buckets[j]).intdiv(2)
if (targetDiff < 0) {
buckets.transfer(j, i, -targetDiff)
} else {
buckets.transfer(i, j, targetDiff)
}
}
}
 
def randomize = { i, j ->
synchronized(buckets) {
def targetLimit = buckets[i] + buckets[j]
def targetI = random.nextInt(targetLimit + 1)
if (targetI < buckets[i]) {
buckets.transfer(i, j, buckets[i] - targetI)
} else {
buckets.transfer(j, i, targetI - buckets[i])
}
}
}
 
Thread.start {
def start = System.currentTimeMillis()
while (start + 10000 > System.currentTimeMillis()) {
def i = random.nextInt(buckets.n)
def j = random.nextInt(buckets.n)
makeCloser(i, j)
}
}
 
Thread.start {
def start = System.currentTimeMillis()
while (start + 10000 > System.currentTimeMillis()) {
def i = random.nextInt(buckets.n)
def j = random.nextInt(buckets.n)
randomize(i, j)
}
}
 
def start = System.currentTimeMillis()
while (start + 10000 > System.currentTimeMillis()) {
synchronized(buckets) {
def sum = buckets.cells.sum()
println "${new Date()}: checksum: ${sum} buckets: ${buckets}"
}
Thread.sleep(500)
}</syntaxhighlight>
 
Output:
<pre>Sat Jan 07 02:24:45 CST 2012: checksum: 2161 buckets: [227, 700, 635, 299, 300]
Sat Jan 07 02:24:46 CST 2012: checksum: 2161 buckets: [477, 365, 364, 478, 477]
Sat Jan 07 02:24:46 CST 2012: checksum: 2161 buckets: [432, 434, 429, 434, 432]
Sat Jan 07 02:24:47 CST 2012: checksum: 2161 buckets: [432, 428, 434, 432, 435]
Sat Jan 07 02:24:48 CST 2012: checksum: 2161 buckets: [432, 433, 432, 432, 432]
Sat Jan 07 02:24:48 CST 2012: checksum: 2161 buckets: [433, 432, 432, 432, 432]
Sat Jan 07 02:24:49 CST 2012: checksum: 2161 buckets: [359, 425, 254, 868, 255]
Sat Jan 07 02:24:49 CST 2012: checksum: 2161 buckets: [433, 432, 432, 432, 432]
Sat Jan 07 02:24:50 CST 2012: checksum: 2161 buckets: [432, 431, 430, 430, 438]
Sat Jan 07 02:24:50 CST 2012: checksum: 2161 buckets: [466, 404, 388, 466, 437]
Sat Jan 07 02:24:51 CST 2012: checksum: 2161 buckets: [476, 569, 365, 386, 365]
Sat Jan 07 02:24:51 CST 2012: checksum: 2161 buckets: [35, 111, 1038, 387, 590]
Sat Jan 07 02:24:52 CST 2012: checksum: 2161 buckets: [423, 341, 341, 423, 633]
Sat Jan 07 02:24:52 CST 2012: checksum: 2161 buckets: [141, 1295, 102, 370, 253]
Sat Jan 07 02:24:53 CST 2012: checksum: 2161 buckets: [683, 188, 345, 638, 307]
Sat Jan 07 02:24:53 CST 2012: checksum: 2161 buckets: [379, 275, 354, 240, 913]
Sat Jan 07 02:24:54 CST 2012: checksum: 2161 buckets: [894, 515, 455, 234, 63]
Sat Jan 07 02:24:54 CST 2012: checksum: 2161 buckets: [306, 507, 793, 507, 48]
Sat Jan 07 02:24:55 CST 2012: checksum: 2161 buckets: [463, 462, 240, 632, 364]
Sat Jan 07 02:24:55 CST 2012: checksum: 2161 buckets: [204, 162, 223, 996, 576]</pre>
 
=={{header|Haskell}}==
Line 711 ⟶ 1,831:
So, at any given time, the current value map is either in the MVar or being examined or replaced by one thread, but not both. The IntMap held by the MVar is a pure immutable data structure (<code>adjust</code> returns a modified version), so there is no problem from that the ''display'' task puts the value back before it is done printing.
 
<langsyntaxhighlight lang="haskell">module AtomicUpdates (main) where
 
import Control.Concurrent (forkIO, threadDelay)
Line 779 ⟶ 1,899:
forkIO (roughen buckets)
forkIO (smooth buckets)
display buckets</langsyntaxhighlight>
 
Sample output:
Line 796 ⟶ 1,916:
80 ***** ** ******** *** * *** ** ** *** * * ***** **** *** *** * ** *** *** * *** * * **
40 ******** ****************** ************************************************************** *******
 
==Icon and {{header|Unicon}}==
 
The following only works in Unicon:
 
<syntaxhighlight lang="unicon">global mtx
 
procedure main(A)
nBuckets := integer(A[1]) | 10
nShows := integer(A[2]) | 4
showBuckets := A[3]
mtx := mutex()
every !(buckets := list(nBuckets)) := ?100
 
thread repeat {
every (b1|b2) := ?nBuckets # OK if same!
critical mtx: xfer((buckets[b1] - buckets[b2])/2, b1, b2)
}
thread repeat {
every (b1|b2) := ?nBuckets # OK if same!
critical mtx: xfer(integer(?buckets[b1]), b1, b2)
}
wait(thread repeat {
delay(500)
critical mtx: {
every (sum := 0) +:= !buckets
writes("Sum: ",sum)
if \showBuckets then every writes(" -> "|right(!buckets, 4))
}
write()
if (nShows -:= 1) <= 0 then break
})
end
 
procedure xfer(x,b1,b2)
buckets[b1] -:= x
buckets[b2] +:= x
end</syntaxhighlight>
 
Sample run:
 
<pre>
->au 20 10 yes
Sum: 973 -> 48 49 48 49 49 49 48 48 49 49 49 49 48 49 48 49 48 49 49 49
Sum: 973 -> 49 49 48 49 49 48 49 49 49 48 48 49 49 48 49 49 48 48 49 49
Sum: 973 -> 49 49 49 48 49 48 48 49 49 49 49 49 49 48 49 48 48 48 49 49
Sum: 973 -> 49 49 49 48 48 48 48 48 49 49 49 49 49 49 49 49 49 48 49 48
Sum: 973 -> 48 49 48 49 49 49 49 48 49 49 48 48 48 49 48 49 49 49 49 49
Sum: 973 -> 70 51 49 31 87 51 53 51 48 50 88 12 43 39 50 46 50 0 53 51
Sum: 973 -> 11 15 83 95 3 53 145 0 8 120 9 9 10 5 45 122 38 70 2 130
Sum: 973 -> 12 260 17 3 45 13 9 4 46 71 18 41 15 68 104 53 18 104 44 28
Sum: 973 -> 49 48 49 49 49 48 49 48 49 49 49 49 49 48 49 48 49 48 48 49
Sum: 973 -> 140 47 32 47 32 60 227 0 48 32 78 15 36 135 8 16 0 8 11 1
->
</pre>
 
=={{header|Java}}==
{{works with|Java|8+}}
<syntaxhighlight lang="java">import java.util.Arrays;
import java.util.concurrent.ThreadLocalRandom;
 
public class AtomicUpdates {
 
private static final int NUM_BUCKETS = 10;
 
public static class Buckets {
private final int[] data;
 
public Buckets(int[] data) {
this.data = data.clone();
}
 
public int getBucket(int index) {
synchronized (data) {
return data[index];
}
}
 
public int transfer(int srcIndex, int dstIndex, int amount) {
if (amount < 0)
throw new IllegalArgumentException("negative amount: " + amount);
if (amount == 0)
return 0;
 
synchronized (data) {
if (data[srcIndex] - amount < 0)
amount = data[srcIndex];
if (data[dstIndex] + amount < 0)
amount = Integer.MAX_VALUE - data[dstIndex];
if (amount < 0)
throw new IllegalStateException();
data[srcIndex] -= amount;
data[dstIndex] += amount;
return amount;
}
}
 
public int[] getBuckets() {
synchronized (data) {
return data.clone();
}
}
}
 
private static long getTotal(int[] values) {
long total = 0;
for (int value : values) {
total += value;
}
return total;
}
 
public static void main(String[] args) {
ThreadLocalRandom rnd = ThreadLocalRandom.current();
 
int[] values = new int[NUM_BUCKETS];
for (int i = 0; i < values.length; i++)
values[i] = rnd.nextInt() & Integer.MAX_VALUE;
System.out.println("Initial Array: " + getTotal(values) + " " + Arrays.toString(values));
 
Buckets buckets = new Buckets(values);
new Thread(() -> equalize(buckets), "equalizer").start();
new Thread(() -> transferRandomAmount(buckets), "transferrer").start();
new Thread(() -> print(buckets), "printer").start();
}
 
private static void transferRandomAmount(Buckets buckets) {
ThreadLocalRandom rnd = ThreadLocalRandom.current();
while (true) {
int srcIndex = rnd.nextInt(NUM_BUCKETS);
int dstIndex = rnd.nextInt(NUM_BUCKETS);
int amount = rnd.nextInt() & Integer.MAX_VALUE;
buckets.transfer(srcIndex, dstIndex, amount);
}
}
 
private static void equalize(Buckets buckets) {
ThreadLocalRandom rnd = ThreadLocalRandom.current();
while (true) {
int srcIndex = rnd.nextInt(NUM_BUCKETS);
int dstIndex = rnd.nextInt(NUM_BUCKETS);
int amount = (buckets.getBucket(srcIndex) - buckets.getBucket(dstIndex)) / 2;
if (amount >= 0)
buckets.transfer(srcIndex, dstIndex, amount);
}
}
 
private static void print(Buckets buckets) {
while (true) {
long nextPrintTime = System.currentTimeMillis() + 3000;
long now;
while ((now = System.currentTimeMillis()) < nextPrintTime) {
try {
Thread.sleep(nextPrintTime - now);
} catch (InterruptedException e) {
return;
}
}
 
int[] bucketValues = buckets.getBuckets();
System.out.println("Current values: " + getTotal(bucketValues) + " " + Arrays.toString(bucketValues));
}
}
}</syntaxhighlight>
 
{{out}}
<pre>Initial Array: 8345792262 [143255168, 196076270, 933397723, 1556699232, 1050802212, 538674858, 1196357020, 738586704, 726124301, 1265818774]
Current values: 8345792262 [0, 1874588555, 1422104978, 1646554792, 272895092, 0, 1100055274, 562892928, 0, 1466700643]
Current values: 8345792262 [0, 938536756, 1022153269, 802097042, 834165196, 893056852, 1022153268, 985683168, 985683168, 862263543]
Current values: 8345792262 [828663081, 828663080, 800738961, 653833491, 926105549, 856587200, 1235929058, 653833491, 780719176, 780719175]
Current values: 8345792262 [834986940, 835010170, 833752099, 835010170, 834668841, 834620567, 833370581, 835083486, 834620567, 834668841]
Current values: 8345792262 [0, 249877205, 1201027166, 2147483647, 0, 966988101, 725353114, 107211829, 2147483647, 800367553]
Current values: 8345792262 [789241957, 389741912, 898370461, 1824723292, 389741912, 898370462, 1824723293, 434230374, 896648599, 0]
Current values: 8345792262 [290197046, 76068608, 2147483647, 185783029, 646610948, 187523099, 1387188383, 0, 2147483647, 1277453855]
Current values: 8345792262 [0, 0, 1594297983, 1972188797, 0, 2147483647, 0, 2147483647, 92403769, 391934419]
Current values: 8345792262 [330331828, 330331828, 2147483647, 515452290, 2010486407, 0, 515452290, 0, 348770325, 2147483647]
...</pre>
 
Commenting out either of the threads mutating the buckets shows that they work properly.
 
=={{header|Julia}}==
<syntaxhighlight lang="julia">using StatsBase
 
function runall()
nbuckets = 16
unfinish = true
spinner = ReentrantLock()
buckets = rand(1:99, nbuckets)
totaltrans = 0
 
bucketsum() = sum(buckets)
smallpause() = sleep(rand() / 2000)
picktwo() = (samplepair(nbuckets)...)
function equalizer()
while unfinish
smallpause()
if trylock(spinner)
i, j = picktwo()
sm = buckets[i] + buckets[j]
m = fld(sm + 1, 2)
buckets[i], buckets[j] = m, sm - m
totaltrans += 1
unlock(spinner)
end
end
end
function redistributor()
while unfinish
smallpause()
if trylock(spinner)
i, j = picktwo()
sm = buckets[i] + buckets[j]
buckets[i] = rand(0:sm)
buckets[j] = sm - buckets[i]
totaltrans += 1
unlock(spinner)
end
end
end
function accountant()
count = 0
while count < 16
smallpause()
if trylock(spinner)
println("Current state of buckets: $buckets. Total in buckets: $(bucketsum())")
unlock(spinner)
count += 1
sleep(1)
end
end
unfinish = false
end
t = time()
@async equalizer()
@async redistributor()
@async accountant()
while unfinish sleep(0.25) end
println("Total transactions: $totaltrans ($(round(Int, totaltrans / (time() - t))) unlocks per second).")
end
 
runall()</syntaxhighlight>
 
{{out}}
<pre>Current state of buckets: [56, 26, 34, 57, 26, 25, 39, 91, 53, 46, 96, 67, 86, 49, 2, 85]. Total in buckets: 838
Current state of buckets: [62, 32, 90, 50, 9, 43, 16, 71, 67, 99, 22, 44, 63, 85, 78, 7]. Total in buckets: 838
Current state of buckets: [58, 25, 41, 30, 9, 79, 42, 43, 32, 66, 110, 123, 90, 35, 13, 42]. Total in buckets: 838
Current state of buckets: [86, 63, 70, 21, 41, 69, 30, 29, 38, 40, 12, 28, 85, 13, 127, 86]. Total in buckets: 838
Current state of buckets: [45, 32, 26, 30, 45, 9, 86, 200, 31, 45, 9, 23, 60, 64, 79, 54]. Total in buckets: 838
Current state of buckets: [68, 16, 89, 104, 15, 35, 15, 23, 91, 92, 29, 27, 33, 21, 136, 44]. Total in buckets: 838
Current state of buckets: [13, 72, 8, 25, 27, 62, 134, 33, 78, 79, 7, 22, 132, 73, 12, 61]. Total in buckets: 838
Current state of buckets: [97, 78, 16, 90, 90, 69, 0, 22, 26, 84, 23, 22, 78, 69, 32, 42]. Total in buckets: 838
Current state of buckets: [3, 105, 99, 69, 70, 8, 50, 32, 17, 69, 53, 1, 68, 66, 64, 64]. Total in buckets: 838
Current state of buckets: [27, 181, 9, 5, 66, 16, 60, 56, 66, 140, 43, 29, 51, 59, 1, 29]. Total in buckets: 838
Current state of buckets: [45, 108, 45, 28, 58, 108, 86, 41, 45, 29, 57, 11, 54, 23, 52, 48]. Total in buckets: 838
Current state of buckets: [76, 45, 47, 75, 62, 34, 73, 27, 102, 64, 32, 51, 55, 32, 43, 20]. Total in buckets: 838
Current state of buckets: [35, 69, 41, 34, 29, 79, 82, 72, 71, 65, 34, 67, 68, 14, 33, 45]. Total in buckets: 838
Current state of buckets: [85, 53, 53, 26, 45, 53, 84, 99, 48, 50, 27, 52, 60, 79, 13, 11]. Total in buckets: 838
Current state of buckets: [49, 63, 24, 38, 64, 79, 75, 70, 69, 68, 50, 74, 12, 60, 6, 37]. Total in buckets: 838
Current state of buckets: [32, 20, 82, 70, 54, 41, 87, 15, 15, 44, 82, 55, 17, 33, 87, 104]. Total in buckets: 838
Total transactions: 26751 (1639 unlocks per second).</pre>
 
=={{header|Kotlin}}==
{{trans|Java}}
<syntaxhighlight lang="scala">// version 1.2.0
 
import java.util.concurrent.ThreadLocalRandom
import kotlin.concurrent.thread
 
const val NUM_BUCKETS = 10
 
class Buckets(data: IntArray) {
private val data = data.copyOf()
 
operator fun get(index: Int) = synchronized(data) { data[index] }
 
fun transfer(srcIndex: Int, dstIndex: Int, amount: Int): Int {
if (amount < 0) {
throw IllegalArgumentException("Negative amount: $amount")
}
if (amount == 0) return 0
synchronized(data) {
var a = amount
if (data[srcIndex] - a < 0) a = data[srcIndex]
if (data[dstIndex] + a < 0) a = Int.MAX_VALUE - data[dstIndex]
if (a < 0) throw IllegalStateException()
data[srcIndex] -= a
data[dstIndex] += a
return a
}
}
 
val buckets get() = synchronized(data) { data.copyOf() }
 
fun transferRandomAmount() {
val rnd = ThreadLocalRandom.current()
while (true) {
val srcIndex = rnd.nextInt(NUM_BUCKETS)
val dstIndex = rnd.nextInt(NUM_BUCKETS)
val amount = rnd.nextInt() and Int.MAX_VALUE
transfer(srcIndex, dstIndex, amount)
}
}
 
fun equalize() {
val rnd = ThreadLocalRandom.current()
while (true) {
val srcIndex = rnd.nextInt(NUM_BUCKETS)
val dstIndex = rnd.nextInt(NUM_BUCKETS)
val amount = (this[srcIndex] - this[dstIndex]) / 2
if (amount >= 0) transfer(srcIndex, dstIndex, amount)
}
}
 
fun print() {
while (true) {
val nextPrintTime = System.currentTimeMillis() + 3000
while (true) {
val now = System.currentTimeMillis()
if (now >= nextPrintTime) break
try {
Thread.sleep(nextPrintTime - now)
}
catch (e: InterruptedException) {
return
}
}
val bucketValues = buckets
println("Current values: ${bucketValues.total} ${bucketValues.asList()}")
}
}
}
 
val IntArray.total: Long get() {
var sum = 0L
for (d in this) sum += d
return sum
}
 
fun main(args: Array<String>) {
val rnd = ThreadLocalRandom.current()
val values = IntArray(NUM_BUCKETS) { rnd.nextInt() and Int.MAX_VALUE }
println("Initial array: ${values.total} ${values.asList()}")
val buckets = Buckets(values)
thread(name = "equalizer") { buckets.equalize() }
thread(name = "transferrer") { buckets.transferRandomAmount() }
thread(name = "printer") { buckets.print() }
}</syntaxhighlight>
 
Sample output:
<pre>
Initial array: 9412276676 [1252597313, 1908616225, 824662669, 972947315, 2126883821, 405179067, 693458796, 481375538, 396750085, 349805847]
Current values: 9412276676 [2147483647, 844064584, 983174119, 580879514, 1073741823, 666808378, 2147483647, 0, 484320482, 484320482]
Current values: 9412276676 [941221423, 941207347, 941304553, 941221422, 941235585, 941235585, 941225242, 941242321, 941157955, 941225243]
Current values: 9412276676 [941656114, 941197476, 941190372, 941203044, 941187119, 941177701, 941208610, 941038975, 941226893, 941190372]
Current values: 9412276676 [0, 202110459, 2147483647, 1901203310, 2147483647, 1489083519, 0, 234363721, 1290548373, 0]
Current values: 9412276676 [695300460, 2147483647, 1452183187, 2147483647, 0, 0, 0, 570277505, 252064583, 2147483647]
Current values: 9412276676 [941219147, 941226725, 941226725, 941238796, 941219147, 941247715, 941238795, 941234946, 941189734, 941234946]
Current values: 9412276676 [941306524, 941145153, 941241743, 940668167, 942314400, 941491117, 940668168, 941145153, 941306524, 940989727]
Current values: 9412276676 [945548859, 939149475, 935477311, 941294057, 944294715, 940031668, 940860151, 940662863, 940662862, 944294715]
Current values: 9412276676 [941254898, 941342907, 941188859, 941250824, 941250825, 940973864, 941078878, 941373381, 941373381, 941188859]
Current values: 9412276676 [941147294, 941232689, 941132597, 941330728, 941281708, 941236213, 941147294, 941265970, 941236214, 941265969]
......
</pre>
 
=={{header|Lasso}}==
Lasso thread objects are thread-safe by design.
<syntaxhighlight lang="lasso">define atomic => thread {
data
private buckets = staticarray_join(10, void),
private lock = 0
 
public onCreate => {
loop(.buckets->size) => {
.`buckets`->get(loop_count) = math_random(0, 1000)
}
}
 
public buckets => .`buckets`
 
public bucket(index::integer) => .`buckets`->get(#index)
 
public transfer(source::integer, dest::integer, amount::integer) => {
#source == #dest
? return
 
#amount = math_min(#amount, .`buckets`->get(#source))
.`buckets`->get(#source) -= #amount
.`buckets`->get(#dest) += #amount
}
 
public numBuckets => .`buckets`->size
 
public lock => {
.`lock` == 1
? return false
 
.`lock` = 1
return true
}
public unlock => {
.`lock` = 0
}
}
 
local(initial_total) = (with b in atomic->buckets sum #b)
local(total) = #initial_total
 
// Make 2 buckets close to equal
local(_) = split_thread => {
local(bucket1) = math_random(1, atomic->numBuckets)
local(bucket2) = math_random(1, atomic->numBuckets)
local(value1) = atomic->bucket(#bucket1)
local(value2) = atomic->bucket(#bucket2)
 
if(#value1 >= #value2) => {
atomic->transfer(#bucket1, #bucket2, (#value1 - #value2) / 2)
else
atomic->transfer(#bucket2, #bucket1, (#value2 - #value1) / 2)
}
 
currentCapture->restart
}
 
// Randomly distribute 2 buckets
local(_) = split_thread => {
local(bucket1) = math_random(1, atomic->numBuckets)
local(bucket2) = math_random(1, atomic->numBuckets)
local(value1) = atomic->bucket(#bucket1)
 
atomic->transfer(#bucket1, #bucket2, math_random(1, #value1))
 
currentCapture->restart
}
 
local(buckets)
while(#initial_total == #total) => {
sleep(2000)
#buckets = atomic->buckets
#total = with b in #buckets sum #b
stdoutnl(#buckets->asString + " -- total: " + #total)
}
stdoutnl(`ERROR: totals no longer match: ` + #initial_total + ', ' + #total)</syntaxhighlight>
 
{{out}}
<pre>staticarray(130, 399, 339, 0, 444, 444, 618, 872, 390, 23) -- total: 3659
staticarray(233, 538, 461, 117, 389, 110, 232, 517, 633, 429) -- total: 3659
staticarray(129, 648, 494, 809, 823, 132, 425, 131, 58, 10) -- total: 3659
staticarray(255, 484, 53, 261, 484, 264, 336, 521, 211, 790) -- total: 3659
staticarray(464, 16, 463, 1043, 470, 177, 369, 486, 41, 130) -- total: 3659
staticarray(281, 717, 341, 716, 50, 17, 129, 247, 964, 197) -- total: 3659
staticarray(423, 509, 51, 458, 265, 423, 292, 458, 661, 119) -- total: 3659</pre>
 
=={{header|Logtalk}}==
The following example can be found in the Logtalk distribution and is used here with permission. Works when using SWI-Prolog, XSB, or YAP as the backend compiler.
<syntaxhighlight lang="logtalk">
:- object(buckets).
 
:- threaded.
 
:- public([start/0, start/4]).
 
% bucket representation
:- private(bucket_/2).
:- dynamic(bucket_/2).
 
% use the same mutex for all the predicates that access the buckets
:- private([bucket/2, buckets/1, transfer/3]).
:- synchronized([bucket/2, buckets/1, transfer/3]).
 
start :-
% by default, create ten buckets with initial random integer values
% in the interval [0, 10[ and print their contents ten times
start(10, 0, 10, 10).
 
start(N, Min, Max, Samples) :-
% create the buckets with random values in the
% interval [Min, Max[ and return their sum
create_buckets(N, Min, Max, Sum),
write('Sum of all bucket values: '), write(Sum), nl, nl,
% use competitive or-parallelism for the three loops such that
% the computations terminate when the display loop terminates
threaded((
display_loop(Samples)
; match_loop(N)
; redistribute_loop(N)
)).
 
create_buckets(N, Min, Max, Sum) :-
% remove all exisiting buckets
retractall(bucket_(_,_)),
% create the new buckets
create_buckets(N, Min, Max, 0, Sum).
 
create_buckets(0, _, _, Sum, Sum) :-
!.
create_buckets(N, Min, Max, Sum0, Sum) :-
random::random(Min, Max, Value),
asserta(bucket_(N,Value)),
M is N - 1,
Sum1 is Sum0 + Value,
create_buckets(M, Min, Max, Sum1, Sum).
 
bucket(Bucket, Value) :-
bucket_(Bucket, Value).
 
buckets(Values) :-
findall(Value, bucket_(_, Value), Values).
 
transfer(Origin, _, Origin) :-
!.
transfer(Origin, Delta, Destin) :-
retract(bucket_(Origin, OriginValue)),
retract(bucket_(Destin, DestinValue)),
% the buckets may have changed between the access to its
% values and the calling of this transfer predicate; thus,
% we must ensure that we're transfering a legal amount
Amount is min(Delta, OriginValue),
NewOriginValue is OriginValue - Amount,
NewDestinValue is DestinValue + Amount,
assertz(bucket_(Origin, NewOriginValue)),
assertz(bucket_(Destin, NewDestinValue)).
 
match_loop(N) :-
% randomly select two buckets
M is N + 1,
random::random(1, M, Bucket1),
random::random(1, M, Bucket2),
% access their contents
bucket(Bucket1, Value1),
bucket(Bucket2, Value2),
% make their new values approximately equal
Delta is truncate(abs(Value1 - Value2)/2),
( Value1 > Value2 ->
transfer(Bucket1, Delta, Bucket2)
; Value1 < Value2 ->
transfer(Bucket2, Delta, Bucket1)
; true
),
match_loop(N).
 
redistribute_loop(N) :-
% randomly select two buckets
M is N + 1,
random::random(1, M, FromBucket),
random::random(1, M, ToBucket),
% access bucket from where we transfer
bucket(FromBucket, Current),
Limit is Current + 1,
random::random(0, Limit, Delta),
transfer(FromBucket, Delta, ToBucket),
redistribute_loop(N).
display_loop(0) :-
!.
display_loop(N) :-
buckets(Values),
write(Values), nl,
thread_sleep(2),
M is N - 1,
display_loop(M).
 
:- end_object.
</syntaxhighlight>
 
Sample output:
 
<syntaxhighlight lang="logtalk">
?- buckets::start.
Sum of all bucket values: 52
 
[4,6,9,5,3,5,9,7,4,0]
[5,3,6,3,9,5,5,6,2,8]
[2,2,3,13,5,5,2,8,6,6]
[7,4,7,1,1,1,5,11,8,7]
[8,5,8,4,4,3,4,1,3,12]
[2,4,8,6,11,6,6,7,1,1]
[2,12,3,2,6,5,0,9,7,6]
[2,6,3,3,16,3,2,3,7,7]
[6,0,4,0,23,1,1,4,2,11]
[11,6,10,4,0,4,5,5,4,3]
true.
</syntaxhighlight>
 
=={{header|Mathematica}} / {{header|Wolfram Language}}==
<syntaxhighlight lang="mathematica">transfer[bucks_, src_, dest_, n_] :=
ReplacePart[
bucks, {src -> Max[bucks[[src]] - n, 0],
dest -> bucks[[dest]] + Min[bucks[[src]], n]}];
DistributeDefinitions[transfer];
SetSharedVariable[bucks, comp];
bucks = RandomInteger[10, 20];
comp = True;
Print["Original sum: " <> IntegerString[Plus @@ bucks]];
Print[Dynamic["Current sum: " <> IntegerString[Plus @@ bucks]]];
WaitAll[{ParallelSubmit[
While[True, While[! comp, Null]; comp = False;
Module[{a = RandomInteger[{1, 20}], b = RandomInteger[{1, 20}]},
bucks = transfer[bucks, Max[a, b], Min[a, b],
Floor[Abs[bucks[[a]] - bucks[[b]]]/2]]]; comp = True]],
ParallelSubmit[
While[True, While[! comp, Null]; comp = False;
Module[{src = RandomInteger[{1, 20}],
dest = RandomInteger[{1, 20}]},
bucks = transfer[bucks, src, dest,
RandomInteger[{1, bucks[[src]]}]]]; comp = True]]}];</syntaxhighlight>
{{out}}
<pre>Original sum: &lt;number&gt;
Current sum: &lt;same number, stays fixed&gt;</pre>
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.
 
The program must be compiled with option <code>--threads:on</code>.
 
<syntaxhighlight 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()</syntaxhighlight>
 
{{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}}==
Uses a lock for every bucket. Enforces a locking order to avoid deadlocks.
 
<langsyntaxhighlight lang="oz">declare
%%
%% INIT
%%
NBuckets = 100
StartVal = 50
ExpectedSum = (NBuckets * (NBuckets+1)) div 2
ExpectedSum = NBuckets * StartVal
 
%% Makes a tuple and calls Fun for every field
fun {CreateBuckets}
fun {Make Label N Fun}
{Map {List.number 1 NBuckets 1}
R = {Tuple.make Label N}
fun {$ I} bucket('lock':{NewLock} cell:{NewCell I}) end}
in
for I in 1..N do R.I = {Fun} end
R
end
Buckets = {Make buckets NBuckets fun {$} {Cell.new StartVal} end}
Locks = {Make locks NBuckets Lock.new}
LockList = {Record.toList Locks}
 
%%
BucketsList = {CreateBuckets}
%% for O(1) access:DISPLAY
%%
Buckets = {List.toTuple unit BucketsList}
proc {Display}
Snapshot = {WithLocks LockList
fun {$}
{Record.map Buckets Cell.access}
end
}
Sum = {Record.foldL Snapshot Number.'+' 0}
in
{Print Snapshot}
{System.showInfo " sum: "#Sum}
Sum = ExpectedSum %% assert
end
 
%% Calls Fun with multiple locks locked and returns the result of Fun.
%% Returns a procedure which executes Proc while the given bucket is locked
fun {WithLocks Ls Fun}
fun {WithLockedBucket bucket('lock':L cell:C ...) Proc}
proccase {$}Ls of L|Lr then
lock L then {Proc} end
{WithLocks Lr Fun}
end
[] nil then {Fun}
end
end
 
%%
proc {Display}
%% MANIPULATE
Snapshot
%%
proc {Smooth I J}
Diff = @(Buckets.I) - @(Buckets.J) %% reading without lock: by design
Amount = Diff div 4
in
{Transfer I J Amount}
end
 
proc {Roughen I J}
%% Get a consistent snapshot (with all buckets locked)
Amount = @(Buckets.I) div 3 %% reading without lock: by design
{{FoldR BucketsList WithLockedBucket
proc {$}
Snapshot = {Map BucketsList fun {$ bucket(cell:C ...)} @C end}
end
}}
Sum = {FoldL Snapshot Number.'+' 0}
Median = {Nth {Sort Snapshot Value.'<'} NBuckets div 2}
in
{Print Snapshot}Transfer I J Amount}
{System.showInfo " sum: "#Sum#", median: "#Median}
Sum = ExpectedSum %% assert
{Time.delay 500}
{Display}
end
 
%% Atomically transfer an amount from From to To.
%% negative amounts are allowed;
%% Negative amounts are allowed;
%% will never make a bucket negative
%% will never make a bucket negative.
proc {Transfer From To Amount}
if From \= To then
Line 848 ⟶ 2,748:
Bigger = {Max From To}
in
lock BucketsLocks.Smaller.'lock' then
lock BucketsLocks.Bigger.'lock' then
FromBucket = Buckets.From.cell
ToBucket = Buckets.To.cell
NewFromValNewFromValue = @FromBucket - Amount
NewToValNewToValue = @ToBucket + Amount
in
if NewFromValNewFromValue >= 0 andthen NewToValNewToValue >= 0 then
FromBucket := NewFromValNewFromValue
ToBucket := NewToValNewToValue
end
end
Line 864 ⟶ 2,764:
end
 
%% Returns a random bucket index.
proc {Smooth I J}
Diff = @(Buckets.I.cell) - @(Buckets.J.cell)
in
{Transfer I J Diff div 4}
end
 
proc {Roughen I J}
IVal = @(Buckets.I.cell)
in
{Transfer I J IVal div 3}
end
 
fun {Pick}
{OS.rand} mod NBuckets + 1
end
in
%%
%% START
%%
thread for do {Smooth {Pick} {Pick}} end end
thread for do {Roughen {Pick} {Pick}} end end
for do {Display} {Time.delay 50} end</langsyntaxhighlight>
 
Sample output:
<syntaxhighlight lang="oz">buckets(50 50 50 50 50 50 50 50 50 50 ,,,) sum: 5000
<lang oz>1|2|3|4|5|6|7|8|9|10|,,,|,,, sum: 5050, median: 50
buckets(24 68 58 43 78 85 43 66 14 48 ,,,) sum: 5000
50|51|34|52|52|50|50|51|49|49|,,,|,,, sum: 5050, median: 51
buckets(36 33 59 38 39 23 55 51 43 45 ,,,) sum: 5000
50|50|31|54|51|49|50|51|52|66|,,,|,,, sum: 5050, median: 50
buckets(64 32 62 26 50 82 38 70 16 43 ,,,) sum: 5000
56|44|60|16|33|66|40|28|33|64|,,,|,,, sum: 5050, median: 45
50|buckets(51|50|50|50|49| 51| 49| 50| 51|,,,| 51 51 49 49 49 ,,,) sum: 5050, median: 515000
buckets(43 28 27 60 77 41 36 48 72 70 ,,,) sum: 5000
79|41|63|57|40|45|50|180|51|41|,,,|,,, sum: 5050, median: 44
...</syntaxhighlight>
30|40|62|18|72|16|45|46|16|10|,,,|,,, sum: 5050, median: 43</lang>
 
=={{header|PARI/GP}}==
GP is not able to do atomic updates. PARI does atomic updates just like [[#C|C]].
 
=={{header|Perl}}==
<syntaxhighlight lang="perl">use strict;
use 5.10.0;
 
use threads 'yield';
use threads::shared;
 
my @a :shared = (100) x 10;
my $stop :shared = 0;
 
sub pick2 {
my $i = int(rand(10));
my $j;
$j = int(rand(10)) until $j != $i;
($i, $j)
}
 
sub even {
lock @a;
my ($i, $j) = pick2;
my $sum = $a[$i] + $a[$j];
$a[$i] = int($sum / 2);
$a[$j] = $sum - $a[$i];
}
 
sub rand_move {
lock @a;
my ($i, $j) = pick2;
 
my $x = int(rand $a[$i]);
$a[$i] -= $x;
$a[$j] += $x;
}
 
sub show {
lock @a;
my $sum = 0;
$sum += $_ for (@a);
printf "%4d", $_ for @a;
print " total $sum\n";
}
 
my $t1 = async { even until $stop }
my $t2 = async { rand_move until $stop }
my $t3 = async {
for (1 .. 10) {
show;
sleep(1);
}
$stop = 1;
};
 
$t1->join; $t2->join; $t3->join;</syntaxhighlight>
 
=={{header|Phix}}==
<!--<syntaxhighlight lang="phix">(notonline)-->
<span style="color: #008080;">without</span> <span style="color: #008080;">js</span> <span style="color: #000080;font-style:italic;">-- (no threads or critical sections in JavaScript)</span>
<span style="color: #008080;">constant</span> <span style="color: #000000;">nBuckets</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">20</span>
<span style="color: #004080;">sequence</span> <span style="color: #000000;">buckets</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">tagset</span><span style="color: #0000FF;">(</span><span style="color: #000000;">nBuckets</span><span style="color: #0000FF;">)</span> <span style="color: #000080;font-style:italic;">-- {1,2,3,..,20}</span>
<span style="color: #008080;">constant</span> <span style="color: #000000;">bucket_cs</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">init_cs</span><span style="color: #0000FF;">()</span> <span style="color: #000080;font-style:italic;">-- critical section</span>
<span style="color: #004080;">atom</span> <span style="color: #000000;">equals</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">rands</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0</span> <span style="color: #000080;font-style:italic;">-- operation counts</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">terminate</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0</span> <span style="color: #000080;font-style:italic;">-- control flag</span>
<span style="color: #008080;">procedure</span> <span style="color: #000000;">mythreads</span><span style="color: #0000FF;">(</span><span style="color: #004080;">integer</span> <span style="color: #000000;">eq</span><span style="color: #0000FF;">)</span>
<span style="color: #000080;font-style:italic;">-- if eq then equalise else randomise</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">b1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">b2</span><span style="color: #0000FF;">,</span><span style="color: #000000;">amt</span>
<span style="color: #008080;">while</span> <span style="color: #008080;">not</span> <span style="color: #000000;">terminate</span> <span style="color: #008080;">do</span>
<span style="color: #000000;">b1</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">rand</span><span style="color: #0000FF;">(</span><span style="color: #000000;">nBuckets</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">b2</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">rand</span><span style="color: #0000FF;">(</span><span style="color: #000000;">nBuckets</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">b1</span><span style="color: #0000FF;">!=</span><span style="color: #000000;">b2</span> <span style="color: #008080;">then</span> <span style="color: #000080;font-style:italic;">-- (test not actually needed)</span>
<span style="color: #7060A8;">enter_cs</span><span style="color: #0000FF;">(</span><span style="color: #000000;">bucket_cs</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">eq</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">amt</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">floor</span><span style="color: #0000FF;">((</span><span style="color: #000000;">buckets</span><span style="color: #0000FF;">[</span><span style="color: #000000;">b1</span><span style="color: #0000FF;">]-</span><span style="color: #000000;">buckets</span><span style="color: #0000FF;">[</span><span style="color: #000000;">b2</span><span style="color: #0000FF;">])/</span><span style="color: #000000;">2</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">equals</span> <span style="color: #0000FF;">+=</span> <span style="color: #000000;">1</span>
<span style="color: #008080;">else</span>
<span style="color: #000000;">amt</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">rand</span><span style="color: #0000FF;">(</span><span style="color: #000000;">buckets</span><span style="color: #0000FF;">[</span><span style="color: #000000;">b1</span><span style="color: #0000FF;">]+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">)-</span><span style="color: #000000;">1</span>
<span style="color: #000000;">rands</span> <span style="color: #0000FF;">+=</span> <span style="color: #000000;">1</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #000000;">buckets</span><span style="color: #0000FF;">[</span><span style="color: #000000;">b1</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">-=</span> <span style="color: #000000;">amt</span>
<span style="color: #000000;">buckets</span><span style="color: #0000FF;">[</span><span style="color: #000000;">b2</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">+=</span> <span style="color: #000000;">amt</span>
<span style="color: #7060A8;">leave_cs</span><span style="color: #0000FF;">(</span><span style="color: #000000;">bucket_cs</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">while</span>
<span style="color: #000000;">exit_thread</span><span style="color: #0000FF;">(</span><span style="color: #000000;">0</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<span style="color: #008080;">procedure</span> <span style="color: #7060A8;">display</span><span style="color: #0000FF;">()</span>
<span style="color: #7060A8;">enter_cs</span><span style="color: #0000FF;">(</span><span style="color: #000000;">bucket_cs</span><span style="color: #0000FF;">)</span>
<span style="color: #0000FF;">?{</span><span style="color: #7060A8;">sum</span><span style="color: #0000FF;">(</span><span style="color: #000000;">buckets</span><span style="color: #0000FF;">),</span><span style="color: #000000;">equals</span><span style="color: #0000FF;">,</span><span style="color: #000000;">rands</span><span style="color: #0000FF;">,</span><span style="color: #000000;">buckets</span><span style="color: #0000FF;">}</span>
<span style="color: #7060A8;">leave_cs</span><span style="color: #0000FF;">(</span><span style="color: #000000;">bucket_cs</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<span style="color: #7060A8;">display</span><span style="color: #0000FF;">()</span>
<span style="color: #008080;">constant</span> <span style="color: #000000;">threads</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">create_thread</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">routine_id</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"mythreads"</span><span style="color: #0000FF;">),{</span><span style="color: #000000;">1</span><span style="color: #0000FF;">}),</span> <span style="color: #000080;font-style:italic;">-- equalise</span>
<span style="color: #000000;">create_thread</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">routine_id</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"mythreads"</span><span style="color: #0000FF;">),{</span><span style="color: #000000;">0</span><span style="color: #0000FF;">})}</span> <span style="color: #000080;font-style:italic;">-- randomise</span>
<span style="color: #008080;">constant</span> <span style="color: #000000;">ESC</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">#1B</span>
<span style="color: #008080;">while</span> <span style="color: #008080;">not</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">get_key</span><span style="color: #0000FF;">(),{</span><span style="color: #000000;">ESC</span><span style="color: #0000FF;">,</span><span style="color: #008000;">'q'</span><span style="color: #0000FF;">,</span><span style="color: #008000;">'Q'</span><span style="color: #0000FF;">})</span> <span style="color: #008080;">do</span>
<span style="color: #7060A8;">sleep</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">)</span>
<span style="color: #7060A8;">display</span><span style="color: #0000FF;">()</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">while</span>
<span style="color: #000000;">terminate</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">1</span>
<span style="color: #000000;">wait_thread</span><span style="color: #0000FF;">(</span><span style="color: #000000;">threads</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">delete_cs</span><span style="color: #0000FF;">(</span><span style="color: #000000;">bucket_cs</span><span style="color: #0000FF;">)</span>
<!--</syntaxhighlight>-->
{{out}}
<pre>
{210,0,0,{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20}}
{210,1326977,1619458,{14,17,10,7,9,6,8,5,9,7,6,10,14,12,12,14,13,15,12,10}}
{210,2637987,3137483,{10,7,4,31,1,11,5,6,16,11,9,15,9,13,15,5,5,10,6,21}}
{210,3973762,4619906,{22,38,9,17,9,10,12,0,3,0,13,11,2,39,4,11,9,0,0,1}}
{210,5327923,6082436,{1,0,0,9,23,1,33,7,1,43,8,17,1,6,30,0,24,2,3,1}}
{210,6671482,7561288,{12,11,2,9,11,4,11,9,13,9,9,20,19,10,10,8,11,11,12,9}}
{210,7950733,9131581,{7,9,10,8,11,8,13,12,11,5,6,8,11,16,15,14,15,11,11,9}}
{210,9272164,10625022,{4,8,28,2,13,13,6,32,12,5,10,4,28,1,12,9,4,9,4,6}}
{210,10615451,12117282,{10,17,18,2,7,13,10,2,12,4,19,10,18,12,9,5,12,11,8,11}}
{210,11912322,13610386,{10,7,15,11,12,8,12,10,15,14,10,7,9,10,8,11,8,10,13,10}}
{210,13243566,15099214,{8,12,11,7,12,13,13,8,9,9,16,10,10,8,10,10,8,10,13,13}}
</pre>
 
=={{header|PicoLisp}}==
We use database objects (persistent symbols) for the buckets, and
child processes to handle the tasks, as this is the standard way
for general PicoLisp applications.
<syntaxhighlight lang="picolisp">(seed (in "/dev/urandom" (rd 8)))
 
(de *Buckets . 15) # Number of buckets
 
# E/R model
(class +Bucket +Entity)
(rel key (+Key +Number)) # Key 1 .. *Buckets
(rel val (+Number)) # Value 1 .. 999
 
# Create new DB file
(pool (tmp "buckets.db"))
 
# Create *Buckets buckets with values between 1 and 999
(for K *Buckets
(new T '(+Bucket) 'key K 'val (rand 1 999)) )
(commit)
 
# Pick a random bucket
(de pickBucket ()
(db 'key '+Bucket (rand 1 *Buckets)) )
 
# First process
(unless (fork)
(seed *Pid) # Ensure local random sequence
(loop
(let (B1 (pickBucket) B2 (pickBucket)) # Pick two buckets 'B1' and 'B2'
(dbSync) # Atomic DB operation
(let (V1 (; B1 val) V2 (; B2 val)) # Get current values
(cond
((> V1 V2)
(dec> B1 'val) # Make them closer to equal
(inc> B2 'val) )
((> V2 V1)
(dec> B2 'val)
(inc> B1 'val) ) ) )
(commit 'upd) # Close transaction
(wait 1) ) ) )
 
# Second process
(unless (fork)
(seed *Pid) # Ensure local random sequence
(loop
(let (B1 (pickBucket) B2 (pickBucket)) # Pick two buckets 'B1' and 'B2'
(unless (== B1 B2) # Found two different ones?
(dbSync) # Atomic DB operation
(let (V1 (; B1 val) V2 (; B2 val)) # Get current values
(cond
((> V1 V2 0)
(inc> B1 'val) # Redistribute them
(dec> B2 'val) )
((> V2 V1 0)
(inc> B2 'val)
(dec> B1 'val) ) ) )
(commit 'upd) # Close transaction
(wait 1) ) ) ) )
 
# Third process
(unless (fork)
(loop
(let Lst (collect 'key '+Bucket) # Get all buckets
(for This Lst # Print current values
(printsp (: val)) )
(prinl # and total sum
"-- Total: "
(sum '((This) (: val)) Lst) ) )
(wait 2000) ) ) # Sleep two seconds
 
(wait)</syntaxhighlight>
Output:
<pre>70 236 582 30 395 215 525 653 502 825 129 769 722 440 708 -- Total: 6801
0 156 566 352 198 263 0 743 0 1316 58 1180 897 0 1072 -- Total: 6801
0 0 424 101 0 0 0 682 0 1809 0 1549 961 0 1275 -- Total: 6801
0 0 0 0 0 0 0 452 0 2226 0 1838 884 0 1401 -- Total: 6801
54 55 56 55 54 55 54 102 54 2363 54 1816 666 55 1308 -- Total: 6801
198 198 197 196 198 198 197 197 196 1903 197 1438 345 197 946 -- Total: 6801
342 344 343 344 344 342 344 343 343 1278 343 992 343 343 413 -- Total: 6801
^C</pre>
 
=={{header|PureBasic}}==
<syntaxhighlight lang="purebasic">#Buckets=9
#TotalAmount=200
Global Dim Buckets(#Buckets)
Global BMutex=CreateMutex()
Global Quit=#False
 
Procedure max(x,y)
If x>=y: ProcedureReturn x
Else: ProcedureReturn y
EndIf
EndProcedure
 
Procedure Move(WantedAmount, From, Dest)
Protected RealAmount
If from<>Dest
LockMutex(BMutex)
RealAmount=max(0, Buckets(from)-WantedAmount)
Buckets(From)-RealAmount
Buckets(Dest)+RealAmount
UnlockMutex(BMutex)
EndIf
ProcedureReturn RealAmount
EndProcedure
 
Procedure Level(A,B)
Protected i, j, t
If A<>B
LockMutex(BMutex)
t=Buckets(A)+Buckets(B)
i=t/2: j=t-i
Buckets(A)=i
Buckets(B)=j
UnlockMutex(BMutex)
EndIf
EndProcedure
 
Procedure DoInvent(Array A(1))
Protected i, sum
LockMutex(BMutex)
For i=0 To ArraySize(Buckets())
A(i)=Buckets(i)
sum+A(i)
Next i
UnlockMutex(BMutex)
ProcedureReturn sum
EndProcedure
 
Procedure MixingThread(arg)
Repeat
Move(Random(#TotalAmount),Random(#Buckets),Random(#Buckets))
Until Quit
EndProcedure
 
Procedure LevelingThread(arg)
Repeat
Level(Random(#Buckets),Random(#Buckets))
Until Quit
EndProcedure
 
If OpenWindow(0,0,0,100,150,"Atomic updates",#PB_Window_SystemMenu)
Define Thread1=CreateThread(@MixingThread(),0)
Define Thread2=CreateThread(@MixingThread(),0)
Define i, Event
Dim Inventory(#Buckets)
; Set up a small GUI
For i=0 To 9
TextGadget(i, 0,i*15,50, 15,"Bucket #"+Str(i))
Next i
TextGadget(10,55,135,40,15,"=")
AddWindowTimer(0,0,500)
Buckets(0)=#TotalAmount
Repeat
Event=WaitWindowEvent()
If Event=#PB_Event_Timer
i=DoInvent(Inventory())
SetGadgetText(10,"="+Str(i))
For i=0 To #Buckets
SetGadgetText(i, Str(Inventory(i)))
Next i
EndIf
Until Event=#PB_Event_CloseWindow
Quit=#True ; Tell threads to shut down
WaitThread(Thread1): WaitThread(Thread2)
EndIf</syntaxhighlight>
 
=={{header|Python}}==
Line 898 ⟶ 3,082:
This code uses a ''threading.Lock'' to serialize access to the bucket set.
 
<langsyntaxhighlight lang="python">from __future__ import with_statement # required for Python 2.5
import threading
import random
Line 971 ⟶ 3,155:
# wait until all worker threads finish
t1.join()
t2.join()</langsyntaxhighlight>
 
Sample Output:
Line 983 ⟶ 3,167:
0 0 0 22 11 0 13 12 0 0 0 20 0 0 0 = 78
</pre>
 
=={{header|Racket}}==
<syntaxhighlight lang="racket">#lang racket
 
(struct bucket (value [lock #:auto])
#:auto-value #f
#:mutable
#:transparent)
 
(define *buckets* (build-vector 10 (λ (i) (bucket 100))))
 
(define (show-buckets)
(let* ([values (for/list ([b *buckets*]) (bucket-value b))]
[total (apply + values)])
(append values (list '- total))))
 
(define *equalizations* 0)
(define *randomizations* 0)
(define *blocks* 0)
 
(define (show-stats)
(let ([n (length *log*)]
[log (reverse *log*)])
(printf "Equalizations ~a, Randomizations ~a, Transfers: ~a, Blocks ~a\n"
*equalizations* *randomizations* n *blocks*)
(for ([i (in-range 10)])
(define j (min (floor (* i (/ n 9))) (sub1 n)))
(printf "~a (~a). " (add1 i) (add1 j))
(displayln (list-ref log j)))))
(define *log* (list (show-buckets)))
 
(define-syntax-rule (inc! x) (set! x (add1 x)))
 
(define (get-bucket i) (vector-ref *buckets* i))
 
(define (get-value i) (bucket-value (get-bucket i)))
(define (set-value! i v) (set-bucket-value! (get-bucket i) v))
 
(define (locked? i) (bucket-lock (vector-ref *buckets* i)))
(define (lock! i v) (set-bucket-lock! (get-bucket i) v))
(define (unlock! i) (lock! i #f))
 
(define *clamp-lock* #f)
 
(define (clamp i j)
(cond [*clamp-lock* (inc! *blocks*)
#f]
[else (set! *clamp-lock* #t)
(let ([result #f]
[g (gensym)])
(unless (locked? i)
(lock! i g)
(cond [(locked? j) (unlock! i)]
[else (lock! j g)
(set! result #t)]))
(unless result (inc! *blocks*))
(set! *clamp-lock* #f)
result)]))
 
(define (unclamp i j)
(unlock! i)
(unlock! j))
(define (transfer i j amount)
(let* ([lock1 (locked? i)]
[lock2 (locked? j)]
[a (get-value i)]
[b (get-value j)]
[c (- a amount)]
[d (+ b amount)])
(cond [(< c 0) (error 'transfer "Removing too much.")]
[(< d 0) (error 'transfer "Stealing too much.")]
[(and lock1 (equal? lock1 lock2)) (set-value! i c)
(set-value! j d)
(set! *log*
(cons (show-buckets) *log*))]
[else (error 'transfer "Lock problem")])))
(define (equalize i j)
(when (clamp i j)
(let ([a (get-value i)]
[b (get-value j)])
(unless (= a b)
(transfer i j (if (> a b)
(floor (/ (- a b) 2))
(- (floor (/ (- b a) 2)))))
(inc! *equalizations*)))
(unclamp i j)))
(define (randomize i j)
(when (clamp i j)
(let* ([a (get-value i)]
[b (get-value j)]
[t (+ a b)]
[r (if (= t 0) 0 (random t))])
(unless (= r 0)
(transfer i j (- a r))
(inc! *randomizations*)))
(unclamp i j)))
 
(thread (λ () (for ([_ (in-range 500000)]) (equalize (random 10) (random 10)))))
(thread (λ () (for ([_ (in-range 500000)]) (randomize (random 10) (random 10)))))</syntaxhighlight>
 
Sample output:
 
<pre>
> (show-stats)
 
Equalizations 33616, Randomizations 159240, Transfers: 192857, Blocks 579035
1 (1). (100 100 100 100 100 100 100 100 100 100 - 1000)
2 (21429). (100 238 23 36 153 111 86 100 38 115 - 1000)
3 (42858). (162 26 127 39 459 5 40 23 90 29 - 1000)
4 (64286). (16 80 41 307 117 38 251 36 29 85 - 1000)
5 (85715). (96 62 142 7 102 48 150 80 57 256 - 1000)
6 (107143). (69 70 69 69 69 69 298 69 69 149 - 1000)
7 (128572). (56 66 99 23 328 99 116 117 78 18 - 1000)
8 (150000). (23 128 108 110 56 232 69 25 33 216 - 1000)
9 (171429). (27 169 298 9 26 184 134 27 110 16 - 1000)
10 (192857). (54 80 38 52 29 14 42 173 246 272 - 1000)
</pre>
 
=={{header|Raku}}==
(formerly Perl 6)
 
{{trans|Ruby}}
{{works with|Rakudo|2016.07}}
<syntaxhighlight lang="raku" line>#| A collection of non-negative integers, with atomic operations.
class BucketStore {
has $.elems is required;
has @!buckets = ^1024 .pick xx $!elems;
has $lock = Lock.new;
#| Returns an array with the contents of all buckets.
method buckets {
$lock.protect: { [@!buckets] }
}
#| Transfers $amount from bucket at index $from, to bucket at index $to.
method transfer ($amount, :$from!, :$to!) {
return if $from == $to;
$lock.protect: {
my $clamped = $amount min @!buckets[$from];
@!buckets[$from] -= $clamped;
@!buckets[$to] += $clamped;
}
}
}
 
# Create bucket store
my $bucket-store = BucketStore.new: elems => 8;
my $initial-sum = $bucket-store.buckets.sum;
 
# Start a thread to equalize buckets
Thread.start: {
loop {
my @buckets = $bucket-store.buckets;
# Pick 2 buckets, so that $to has not more than $from
my ($to, $from) = @buckets.keys.pick(2).sort({ @buckets[$_] });
# Transfer half of the difference, rounded down
$bucket-store.transfer: ([-] @buckets[$from, $to]) div 2, :$from, :$to;
}
}
 
# Start a thread to distribute values among buckets
Thread.start: {
loop {
my @buckets = $bucket-store.buckets;
# Pick 2 buckets
my ($to, $from) = @buckets.keys.pick(2);
# Transfer a random portion
$bucket-store.transfer: ^@buckets[$from] .pick, :$from, :$to;
}
}
 
# Loop to display buckets
loop {
sleep 1;
my @buckets = $bucket-store.buckets;
my $sum = @buckets.sum;
say "{@buckets.fmt: '%4d'}, total $sum";
if $sum != $initial-sum {
note "ERROR: Total changed from $initial-sum to $sum";
exit 1;
}
}</syntaxhighlight>
 
{{out}}
<pre>
23 52 831 195 1407 809 813 20, total 4150
1172 83 336 306 751 468 615 419, total 4150
734 103 1086 88 313 136 1252 438, total 4150
512 323 544 165 200 3 2155 248, total 4150
...
</pre>
 
=={{header|Ring}}==
<syntaxhighlight lang="ring">
# Project : Atomic updates
 
bucket = list(10)
f2 = 0
for i = 1 to 10
bucket[i] = floor(random(9)*10)
next
a = display("display:")
see nl
a = flatten(a)
see "" + a + nl
a = display("flatten:")
see nl
a = transfer(3,5)
see a + nl
see "19 from 3 to 5: "
a = display(a)
see nl
func display(a)
display = 0
see "" + a + " " + char(9)
for i = 1 to 10
display = display + bucket[i]
see "" + bucket[i] + " "
next
see " total:" + display
return display
func flatten(f)
f1 = floor((f / 10) + 0.5)
for i = 1 to 10
bucket[i] = f1
f2 = f2 + f1
next
bucket[10] = bucket[10] + f - f2
func transfer(a1,a2)
transfer = floor(random(9)/10 * bucket[a1])
bucket[a1] = bucket[a1] - transfer
bucket[a2] = bucket[a2] + transfer
</syntaxhighlight>
Output:
<pre>
display: 60 10 70 60 40 80 90 20 90 20 total:540
flatten: 54 54 54 54 54 54 54 54 54 54 total:540
19 from 3 to 5: 54 54 33 54 75 54 54 54 54 54 total:540
</pre>
 
=={{header|Ruby}}==
<syntaxhighlight lang="ruby">require 'thread'
 
# A collection of buckets, filled with random non-negative integers.
# There are atomic operations to look at the bucket contents, and
# to move amounts between buckets.
class BucketStore
 
# Creates a BucketStore with +nbuckets+ buckets. Fills each bucket
# with a random non-negative integer.
def initialize nbuckets
# Create an array for the buckets
@buckets = (0...nbuckets).map { rand(1024) }
 
# Mutex used to make operations atomic
@mutex = Mutex.new
end
 
# Returns an array with the contents of all buckets.
def buckets
@mutex.synchronize { Array.new(@buckets) }
end
 
# Transfers _amount_ to bucket at array index _destination_,
# from bucket at array index _source_.
def transfer destination, source, amount
# Do nothing if both buckets are same
return nil if destination == source
 
@mutex.synchronize do
# Clamp amount to prevent negative value in bucket
amount = [amount, @buckets[source]].min
 
@buckets[source] -= amount
@buckets[destination] += amount
end
nil
end
end
 
# Create bucket store
bucket_store = BucketStore.new 8
 
# Get total amount in the store
TOTAL = bucket_store.buckets.inject { |a, b| a += b }
 
# Start a thread to equalize buckets
Thread.new do
loop do
# Pick 2 buckets
buckets = bucket_store.buckets
first = rand buckets.length
second = rand buckets.length
 
# Swap buckets so that _first_ has not more than _second_
first, second = second, first if buckets[first] > buckets[second]
 
# Transfer half of the difference, rounded down
bucket_store.transfer first, second, (buckets[second] - buckets[first]) / 2
end
end
 
# Start a thread to distribute values among buckets
Thread.new do
loop do
# Pick 2 buckets
buckets = bucket_store.buckets
first = rand buckets.length
second = rand buckets.length
 
# Transfer random amount to _first_ from _second_
bucket_store.transfer first, second, rand(buckets[second])
end
end
 
# Loop to display buckets
loop do
sleep 1
 
buckets = bucket_store.buckets
 
# Compute the total value in all buckets.
# We calculate this outside BucketStore so BucketStore can't cheat by
# always reporting the same value.
n = buckets.inject { |a, b| a += b }
 
# Display buckets and total
printf "%s, total %d\n", (buckets.map { |v| sprintf "%4d", v }.join " "), n
 
if n != TOTAL
# This should never happen
$stderr.puts "ERROR: Total changed from #{TOTAL} to #{n}"
exit 1
end
end</syntaxhighlight>
 
Sample Output:
<pre> 221 521 331 1186 654 185 521 19, total 3638
455 455 455 455 454 454 455 455, total 3638
455 455 455 455 454 454 455 455, total 3638
755 3 115 10 598 1326 515 316, total 3638 </pre>
 
=={{header|Run BASIC}}==
<syntaxhighlight lang="runbasic">DIM bucket(10)
FOR i = 1 TO 10 : bucket(i) = int(RND(0)*100) : NEXT
 
a = display(" Display:") ' show original array
a = flatten(a) ' flatten the array
a = display(" Flatten:") ' show flattened array
a = transfer(3,5) ' transfer some amount from 3 to 5
a = display(a;" from 3 to 5:") ' Show transfer array
end
 
FUNCTION display(a$)
print a$;" ";chr$(9);
for i = 1 to 10
display = display + bucket(i)
print bucket(i);chr$(9);
next i
print " Total:";display
END FUNCTION
FUNCTION flatten(f)
f1 = int((f / 10) + .5)
for i = 1 to 10
bucket(i) = f1
f2 = f2 + f1
next i
bucket(10) = bucket(10) + f - f2
END FUNCTION
 
 
FUNCTION transfer(a1,a2)
transfer = int(rnd(0) * bucket(a1))
bucket(a1) = bucket(a1) - transfer
bucket(a2) = bucket(a2) + transfer
END FUNCTION</syntaxhighlight>
<pre> Display: 24 50 50 85 63 49 50 91 10 2 Total:474
Flatten: 47 47 47 47 47 47 47 47 47 51 Total:474
19 from 3 to 5: 47 47 28 47 66 47 47 47 47 51 Total:474</pre>
 
=={{header|Rust}}==
{{libheader|rand}}
<syntaxhighlight lang="rust">extern crate rand;
 
use std::sync::{Arc, Mutex};
use std::thread;
use std::cmp;
use std::time::Duration;
 
use rand::Rng;
use rand::distributions::{IndependentSample, Range};
 
trait Buckets {
fn equalize<R:Rng>(&mut self, rng: &mut R);
fn randomize<R:Rng>(&mut self, rng: &mut R);
fn print_state(&self);
}
 
impl Buckets for [i32] {
fn equalize<R:Rng>(&mut self, rng: &mut R) {
let range = Range::new(0,self.len()-1);
let src = range.ind_sample(rng);
let dst = range.ind_sample(rng);
if dst != src {
let amount = cmp::min(((dst + src) / 2) as i32, self[src]);
let multiplier = if amount >= 0 { -1 } else { 1 };
self[src] += amount * multiplier;
self[dst] -= amount * multiplier;
}
}
fn randomize<R:Rng>(&mut self, rng: &mut R) {
let ind_range = Range::new(0,self.len()-1);
let src = ind_range.ind_sample(rng);
let dst = ind_range.ind_sample(rng);
if dst != src {
let amount = cmp::min(Range::new(0,20).ind_sample(rng), self[src]);
self[src] -= amount;
self[dst] += amount;
 
}
}
fn print_state(&self) {
println!("{:?} = {}", self, self.iter().sum::<i32>());
}
}
 
fn main() {
let e_buckets = Arc::new(Mutex::new([10; 10]));
let r_buckets = e_buckets.clone();
let p_buckets = e_buckets.clone();
 
thread::spawn(move || {
let mut rng = rand::thread_rng();
loop {
let mut buckets = e_buckets.lock().unwrap();
buckets.equalize(&mut rng);
}
});
thread::spawn(move || {
let mut rng = rand::thread_rng();
loop {
let mut buckets = r_buckets.lock().unwrap();
buckets.randomize(&mut rng);
}
});
 
let sleep_time = Duration::new(1,0);
loop {
{
let buckets = p_buckets.lock().unwrap();
buckets.print_state();
}
thread::sleep(sleep_time);
}
}</syntaxhighlight>
 
=={{header|Scala}}==
<syntaxhighlight lang="scala">
<lang Scala>
object AtomicUpdates {
 
Line 1,061 ⟶ 3,719:
}
}
</syntaxhighlight>
</lang>
=={{header|Smalltalk}}==
{{works with|Smalltalk/X}}
<syntaxhighlight lang="smalltalk">NUM_BUCKETS := 10.
"create and preset with random data"
buckets := (1 to:NUM_BUCKETS)
collect:[:i | Random nextIntegerBetween:0 and:10000]
as:Array.
count_randomizations := 0.
count_equalizations := 0.
 
printSum :=
[
"the sum must be computed and printed while noone fiddles around"
|snapshot|
snapshot := buckets synchronized:[ buckets copy ].
Transcript showCR: e' {snapshot} sum={snapshot sum}'.
].
 
pickTwo :=
[:action |
"pick two pockets and eval action on it"
|p1 p2|
p1 := Random nextIntegerBetween:1 and:NUM_BUCKETS.
p2 := Random nextIntegerBetween:1 and:NUM_BUCKETS.
buckets synchronized:[ action value:p1 value:p2 ].
].
 
randomize :=
[
pickTwo value:[:p1 :p2 |
"take a random value from p1 and add to p2"
|howMuch|
howMuch := Random nextIntegerBetween:0 and:(buckets at:p1).
buckets at:p1 put:(buckets at:p1)-howMuch.
buckets at:p2 put:(buckets at:p2)+howMuch.
].
count_randomizations := count_randomizations + 1.
].
 
equalize :=
[
pickTwo value:[:p1 :p2 |
"average them"
|diff|
diff := ((buckets at:p1) - (buckets at:p2)) // 2.
buckets at:p1 put:(buckets at:p1)-diff.
buckets at:p2 put:(buckets at:p2)+diff.
].
count_equalizations := count_equalizations + 1.
].
 
"start the show"
randomizer := [ randomize loop ] fork.
equalizer := [ equalize loop ] fork.
 
"every 2 seconds, print the sum"
monitor := [
[
printSum value.
Delay waitFor:2 seconds.
] loop.
] fork.
 
"let it run for 10 seconds, then kill them all"
Delay waitFor:20 seconds.
randomizer terminate.
equalizer terminate.
monitor terminate.
 
Stdout printCR: e'performed {count_equalizations} equalizations and {count_randomizations} randomizations'.</syntaxhighlight>
{{out}}
<pre>#(3940 3940 3940 3940 3939 3940 3940 3940 3940 3939) sum=39398
#(3940 3939 3940 3940 3940 3940 3940 3940 3940 3939) sum=39398
#(3940 3939 3940 3939 3940 3940 3940 3940 3940 3940) sum=39398
#(326 90 19490 831 2668 4840 37 6285 441 4390) sum=39398
#(3940 3940 3939 3940 3940 3940 3940 3940 3940 3939) sum=39398
#(3940 3940 3939 3940 3940 3939 3940 3940 3940 3940) sum=39398
#(1073 499 8808 1094 457 7380 4447 12307 1526 1807) sum=39398
#(10073 494 3913 284 286 105 18599 437 1332 3875) sum=39398
#(7938 7 9691 1853 1709 3566 12374 459 1062 739) sum=39398
#(327 1185 790 9606 5667 477 1260 178 18474 1434) sum=39398
performed 3360635 equalizations and 3060706 randomizations</pre>
Due to the way the CPU is scheduled, there are periods where the equalizer is way ahead, and others, where the randomizer is. Thus, depending on when sampled, the buckets are well equalized at times (running a single core).
 
=={{header|Swift}}==
 
<syntaxhighlight lang="swift">import Foundation
 
final class AtomicBuckets: CustomStringConvertible {
var count: Int {
return buckets.count
}
 
var description: String {
return withBucketsLocked { "\(buckets)" }
}
 
var total: Int {
return withBucketsLocked { buckets.reduce(0, +) }
}
 
private let lock = DispatchSemaphore(value: 1)
 
private var buckets: [Int]
 
subscript(n: Int) -> Int {
return withBucketsLocked { buckets[n] }
}
 
init(with buckets: [Int]) {
self.buckets = buckets
}
 
func transfer(amount: Int, from: Int, to: Int) {
withBucketsLocked {
let transferAmount = buckets[from] >= amount ? amount : buckets[from]
 
buckets[from] -= transferAmount
buckets[to] += transferAmount
}
}
 
private func withBucketsLocked<T>(do: () -> T) -> T {
let ret: T
 
lock.wait()
ret = `do`()
lock.signal()
 
return ret
}
}
 
let bucks = AtomicBuckets(with: [21, 39, 40, 20])
let order = DispatchSource.makeTimerSource()
let chaos = DispatchSource.makeTimerSource()
let printer = DispatchSource.makeTimerSource()
 
printer.setEventHandler {
print("\(bucks) = \(bucks.total)")
}
 
printer.schedule(deadline: .now(), repeating: .seconds(1))
printer.activate()
 
order.setEventHandler {
let (b1, b2) = (Int.random(in: 0..<bucks.count), Int.random(in: 0..<bucks.count))
let (v1, v2) = (bucks[b1], bucks[b2])
 
guard v1 != v2 else {
return
}
 
if v1 > v2 {
bucks.transfer(amount: (v1 - v2) / 2, from: b1, to: b2)
} else {
bucks.transfer(amount: (v2 - v1) / 2, from: b2, to: b1)
}
}
 
order.schedule(deadline: .now(), repeating: .milliseconds(5))
order.activate()
 
chaos.setEventHandler {
let (b1, b2) = (Int.random(in: 0..<bucks.count), Int.random(in: 0..<bucks.count))
 
bucks.transfer(amount: Int.random(in: 0..<(bucks[b1] + 1)), from: b1, to: b2)
}
 
chaos.schedule(deadline: .now(), repeating: .milliseconds(5))
chaos.activate()
 
dispatchMain()</syntaxhighlight>
 
{{out}}
 
<pre>[21, 39, 40, 20] = 120
[14, 28, 46, 32] = 120
[25, 17, 38, 40] = 120
[5, 46, 69, 0] = 120
[22, 52, 24, 22] = 120
[11, 70, 20, 19] = 120
[18, 19, 46, 37] = 120</pre>
 
=={{header|Tcl}}==
Line 1,067 ⟶ 3,908:
<br>
{{works with|Tcl|8.5}}
<langsyntaxhighlight lang="tcl">package require Thread
package require Tk
 
Line 1,168 ⟶ 4,009:
tkwait window .
tsv::set still going 0
thread::broadcast thread::exit</langsyntaxhighlight>
 
=={{header|Wren}}==
{{libheader|Wren-math}}
This is based on the Kotlin entry but has been modified somewhat mainly due to the following factors.
 
Wren-cli doesn't have threads but uses Fibers for concurrent operations in combination with the Scheduler/Timer classes for asynchronous operations.
 
Fibers are cooperatively (rather than preemptively) scheduled and only one fiber can run at a time. Consequently, simultaneous operations are impossible and all operations are therefore atomic by their nature.
<syntaxhighlight lang="wren">import "random" for Random
import "scheduler" for Scheduler
import "timer" for Timer
import "./math" for Nums
 
var Rnd = Random.new()
 
var NUM_BUCKETS = 10
var MAX_VALUE = 9999
 
class Buckets {
construct new(data) {
_data = data.toList
_running = true
}
 
[index] { _data[index] }
 
transfer(srcIndex, dstIndex, amount) {
if (amount < 0) Fiber.abort("Negative amount: %(amount)")
if (amount == 0) return 0
var a = amount
if (_data[srcIndex] - a < 0) a = _data[srcIndex]
if (_data[dstIndex] + a < 0) a = MAX_VALUE - _data[dstIndex]
if (a < 0) Fiber.abort("Negative amount: %(a)")
_data[srcIndex] = _data[srcIndex] - a
_data[dstIndex] = _data[dstIndex] + a
return a
}
 
buckets { _data.toList }
 
transferRandomAmount() {
while (_running) {
var srcIndex = Rnd.int(NUM_BUCKETS)
var dstIndex = Rnd.int(NUM_BUCKETS)
var amount = Rnd.int(MAX_VALUE + 1)
transfer(srcIndex, dstIndex, amount)
Timer.sleep(1)
}
}
 
equalize() {
while (_running) {
var srcIndex = Rnd.int(NUM_BUCKETS)
var dstIndex = Rnd.int(NUM_BUCKETS)
var amount = ((this[srcIndex] - this[dstIndex])/2).truncate
if (amount >= 0) transfer(srcIndex, dstIndex, amount)
Timer.sleep(1)
}
}
 
stop() { _running = false }
 
print() {
Timer.sleep(1000) // one second delay between prints
var bucketValues = buckets
System.print("Current values: %(Nums.sum(bucketValues)) %(bucketValues)")
}
}
 
var values = List.filled(NUM_BUCKETS, 0)
for (i in 0...NUM_BUCKETS) values[i] = Rnd.int(MAX_VALUE + 1)
System.print("Initial array : %(Nums.sum(values)) %(values)")
var buckets = Buckets.new(values)
var count = 0
while (true) {
Scheduler.add {
buckets.equalize()
}
buckets.print()
Scheduler.add {
buckets.transferRandomAmount()
}
buckets.print()
count = count + 2
if (count == 10) { // stop after 10 prints, say
buckets.stop()
break
}
}</syntaxhighlight>
 
{{out}}
Sample run:
<pre>
Initial array : 54458 [3795, 7333, 1896, 7813, 5336, 3828, 7832, 2001, 4856, 9768]
Current values: 54458 [5446, 5446, 5445, 5445, 5446, 5446, 5446, 5446, 5446, 5446]
Current values: 54458 [7868, 0, 25620, 7867, 0, 6511, 0, 0, 3318, 3274]
Current values: 54458 [5290, 4692, 3744, 7575, 5290, 7575, 1804, 11419, 0, 7069]
Current values: 54458 [0, 0, 2285, 4534, 16194, 11666, 16195, 2608, 976, 0]
Current values: 54458 [3435, 9208, 0, 9054, 4589, 5898, 4866, 0, 12819, 4589]
Current values: 54458 [4122, 8125, 6552, 415, 5845, 8125, 415, 7159, 0, 13700]
Current values: 54458 [5134, 0, 0, 17435, 5135, 9417, 7871, 4299, 5167, 0]
Current values: 54458 [2592, 4867, 5708, 6754, 12923, 0, 10013, 6377, 2532, 2692]
Current values: 54458 [1574, 9609, 8627, 0, 1575, 229, 4157, 12565, 12973, 3149]
Current values: 54458 [4146, 213, 5928, 11141, 2865, 6928, 12598, 2286, 2052, 6301]
</pre>
 
=={{header|zkl}}==
Threads and thread safe objects (locks, lists, ints, etc) are built in.
<syntaxhighlight lang="zkl">class B{
const N=10;
var [const]
buckets=(1).pump(N,List).copy(), //(1,2,3...)
lock=Atomic.Lock(), cnt=Atomic.Int();
fcn init{ "Initial sum: ".println(values().sum()); }
fcn transferArb{ // transfer arbitary amount from 1 bucket to another
b1:=(0).random(N); b2:=(0).random(N);
critical(lock){
t:=(0).random(buckets[b1]);
buckets[b1]=buckets[b1]-t; buckets[b2]=buckets[b2]+t;
}
cnt.inc();
}
fcn transferEq{ // try to make two buckets equal
b1:=(0).random(N); b2:=(0).random(N);
critical(lock){
v1:=buckets[b1]; v2:=buckets[b2];
t:=(v1-v2).abs()/2;
if (v1<v2) t = -t;
buckets[b1]=v1-t; buckets[b2]=v2+t;
}
cnt.inc();
}
fcn values{ critical(lock){buckets.copy()} }
}
 
fcn threadA(b){ while(1) { b.transferArb(); } }
fcn threadE(b){ while(1) { b.transferEq(); } }</syntaxhighlight>
<syntaxhighlight lang="zkl">b:=B();
do(10){ threadA.launch(b); } do(10){ threadE.launch(b); }
 
while(1){
v:=b.values();
v.println("-->",v.sum()," ", b.cnt.value," transfers ",
vm.numThreads," threads");
Atomic.sleep(2.5);
}</syntaxhighlight>
{{out}}
<pre>
Initial sum: 55
L(8,8,7,4,2,2,6,4,4,10)-->55 24 transfers 20 threads
L(1,3,5,6,8,8,1,8,10,5)-->55 33755 transfers 20 threads
L(6,5,4,2,7,6,11,1,7,6)-->55 67616 transfers 20 threads
L(5,8,5,5,9,4,4,4,5,6)-->55 101434 transfers 20 threads
L(4,1,6,9,10,4,5,5,4,7)-->55 135013 transfers 20 threads
L(7,6,5,4,5,4,4,7,7,6)-->55 168516 transfers 20 threads
L(2,4,5,3,4,14,1,5,11,6)-->55 202241 transfers 20 threads
L(7,5,2,3,14,8,6,6,1,3)-->55 235660 transfers 20 threads
L(8,7,9,8,7,6,1,1,6,2)-->55 269039 transfers 20 threads
L(7,4,8,17,3,2,1,5,5,3)-->55 302837 transfers 20 threads
L(4,5,4,5,10,5,5,5,3,9)-->55 336642 transfers 20 threads
</pre>
Another solution, using a Pipe as a "holding tank". Pipes are thread safe queues. This code just moves the values to and from the pipe to synchronize changes. The use of this class is the same as above, just change b:=B() to b:=C();
<syntaxhighlight lang="zkl">class C{
const N=10;
var [const]
buckets=(1).pump(N,List).copy(), //(1,2,3...)
pipe = Thread.Pipe(), cnt=Atomic.Int();
fcn init{
pipe.write(buckets);
"Initial sum: ".println(values().sum());
}
fcn transferArb{ // transfer arbitary amount from 1 bucket to another
b1:=(0).random(N); b2:=(0).random(N);
v:=pipe.read();
t:=(0).random(v[b1]); v[b1]=v[b1]-t; v[b2]=v[b2]+t;
pipe.write(v);
cnt.inc();
}
fcn transferEq{ // try to make two buckets equal
b1:=(0).random(N); b2:=(0).random(N);
v:=pipe.read();
v1:=v[b1]; v2:=v[b2]; t:=(v1-v2).abs()/2;
if (v1<v2) t = -t;
v[b1]=v1-t; v[b2]=v2+t;
pipe.write(v);
cnt.inc();
}
fcn values{
v:=pipe.read(); v2:=v.copy(); pipe.write(v);
v2;
}
}</syntaxhighlight>
 
 
{{omit from|AWK}}
Line 1,178 ⟶ 4,212:
{{omit from|M4}}
{{omit from|Make}}
{{omit from|ML/I}}
{{omit from|PlainTeX}}
{{omit from|TI-89 BASIC}} <!-- Does not have concurrency or background processes. -->
{{omit from|XSLT}}
{{omit from|Z80 Assembly|Has no LOCK instruction}}
9,482

edits