Threads
Run work on several CPU cores.
On this page
valk.thread runs work on other OS threads, so CPU-heavy work can use several cores. For work that mostly waits, coroutines are lighter.
Start a thread#
use valk.thread
fn main() {
let worker = thread.Thread[int].start(fn() int {
let sum = 0
each 0..1000 as i : sum += i
return sum
}) ! panic("Could not start a thread")
println(worker.await()) // 499500
}
Thread[T].startruns a closure that returns aT;await()waits and returns the result.thread.start(fn() { ... })is the same for a closure without a result.- Starting a thread can fail with
InitError.
What a thread closure can capture#
A closure that runs on another thread may capture numbers, bools and atomic values. Make an object or string atomic before capturing it; this copies it once, and the copy is read-only:
use valk.thread
class Job {
name: String
size: int
}
fn main() {
let job: atomic Job = Job { name: "resize", size: 3 }
let factor = 10
let worker = thread.Thread[int].start(fn() int {
return job.size * factor
}) ! panic("Could not start a thread")
println(worker.await()) // 30
}
To change data from several threads, use a Lock[T] or another type from Sharing data between threads.
Run many workers#
ThreadGroup[T] starts workers and returns their results in the order they finish:
use valk.thread
fn main() {
let group = thread.ThreadGroup[int].new()
each Array[int]{ 1, 2, 3 } as number {
group.start(fn() int { return number * number }) ! panic("Could not start a worker")
}
let total = 0
while group.has_pending() {
let index, result = group.await_next() ! break
total += result
}
println(total) // 14
}
await_next returns the start index of the finished worker and its result. Use ThreadGroup[void] for workers without a result.
Split a loop over all cores#
thread.parallel_for(count, handler) calls the handler for every index below count, spread over one thread per CPU, and returns when all calls are done:
use valk.thread
fn main() {
let squares: atomic Array[uint] = Array[uint]{ 0 x 100 }
thread.parallel_for(squares.length, fn(i: uint) {
squares[i] = i * i
})
println(squares[9]) // 81
}
Elements of an atomic array of numbers can be assigned from any thread. A third argument sets the number of threads.
Reuse threads for short tasks#
thread.task queues a closure on a small pool of reused threads, which avoids starting a new thread for each small job:
use valk.thread
fn main() {
let results: atomic Array[uint] = Array[uint]{ 0 x 4 }
let tasks = Array[thread.Task]{}
each 0..4 as i {
let index: uint = i
tasks.append(thread.task(fn() { results[index] = index * 10 }) ! panic("Could not queue a task"))
}
each tasks as task : task.await()
println(results[3]) // 30
}
Sleep#
| Function | Pauses |
|---|---|
time.sleep_ms(ms) |
only the current coroutine; others on the thread keep running |
thread.suspend_ms(ms) |
the whole thread, with all its coroutines |
Prefer time.sleep_ms. To pass values between threads while they run, use channels.
