Usage of Atomic, Monitor, and Timer
Usage of Atomic
Examples:
In a multi-threaded program, atomic operations are performed for counting.
import std.sync.*
import std.time.*
import std.collection.*
let count = AtomicInt64(0)
main(): Int64 {
let list = ArrayList<Future<Int64>>()
/* Creates 1000 threads */
for (_ in 0..1000) {
let fut = spawn {
sleep(Duration.millisecond) /* Sleeps for 1 millisecond */
count.fetchAdd(1)
}
list.add(fut)
}
/* Waits for completion of all threads */
for (f in list) {
f.get()
}
var val = count.load()
println("count = ${val}")
return 0
}
Output:
count = 1000
Usage of Monitor(deprecated)
NOTE
It will be deprecated in future releases and condition will be used instead.
Examples:
In different threads, Monitor is used to suspend and awaken threads.
import std.sync.*
var mon = Monitor()
var flag: Bool = true
main(): Int64 {
let fut = spawn {
mon.lock()
while (flag) {
println("New thread: before wait")
mon.wait()
println("New thread: after wait")
}
mon.unlock()
}
/* Sleep for 10 milliseconds so that the new thread can be executed */
sleep(10 * Duration.millisecond)
mon.lock()
println("Main thread: set flag")
flag = false
mon.unlock()
println("Main thread: notify")
mon.lock()
mon.notifyAll()
mon.unlock()
/* Wait for completion of the new thread */
fut.get()
return 0
}
Output:
New thread: before wait
Main thread: set flag
Main thread: notify
New thread: after wait
Usage of Mutex
Examples:
The Mutex lock and unlock operations are used in different threads:
import std.sync.*
var mt = Mutex()
var con = synchronized(mt) { mt.condition() }
var flag: Bool = true
main(): Int64 {
let fut = spawn {
mt.lock()
while (flag) {
println("New thread: before wait")
con.wait()
println("New thread: after wait")
}
mt.unlock()
}
/* Sleep for 10 ms to ensure that a new thread can be executed. */
sleep(10 * Duration.millisecond)
mt.lock()
println("Main thread: set flag")
flag = false
mt.unlock()
println("Main thread: notify")
mt.lock()
con.notifyAll()
mt.unlock()
/* Wait until the execution of the new thread is completed. */
fut.get()
return 0
}
Results:
New thread: before wait
Main thread: set flag
Main thread: notify
New thread: after wait
Usage of Condition
Examples:
Condition is used to suspend and wake up threads.
import std.sync.{Mutex, Condition, AtomicBool}
var mutex = Mutex()
var flag = AtomicBool(true)
main(): Int64 {
let condition: Condition
synchronized(mutex) {
condition = mutex.condition() // Generate a condition instance when a lock is held.
}
let fut = spawn {
synchronized(mutex) {
println("New thread: before wait")
condition.waitUntil {=> !flag.load() } // Suspend the current thread and wait until the value of flag is true.
println("New thread: after wait")
}
}
/* Sleep for 10 ms to ensure that a new thread can be executed. */
sleep(10 * Duration.millisecond)
synchronized(mutex) { // Wait until the sub-thread is suspended.
println("Main thread: set flag")
flag.store(false) // Change the value of flag.
println("Main thread: notify")
condition.notifyAll() // Wake up a worker thread in the waiting state.
}
/* Wait until the execution of the new thread is completed. */
fut.get()
return 0
}
Results:
New thread: before wait
Main thread: set flag
Main thread: notify
New thread: after wait
Usage of Timer
Examples:
Timer is used to create one-off and repetitive tasks.
import std.sync.*
import std.time.{Duration, DurationExtension}
main(): Int64 {
let count = AtomicInt8(0)
Timer.once(50 * Duration.millisecond) { =>
println("run only once")
count.fetchAdd(1)
}
let timer = Timer.repeat(100 * Duration.millisecond, 200 * Duration.millisecond, { =>
println("run repetitively")
count.fetchAdd(10)
})
sleep(Duration.second)
timer.cancel()
sleep(500 * Duration.millisecond)
println("count = ${count.load()}")
0
}
Output:
run only once
run repetitively
run repetitively
run repetitively
run repetitively
run repetitively
count = 51