Spawn, Temporização e Cancelamento
Nesta página
spawn registra uma tarefa no escalonador cooperativo — ela não executa
imediatamente. O escalonador alterna entre tarefas prontas quando o chamador
invoca tick(), every, after, ou ao final do escopo.
A chamada tick() é equivalente a coroutine.yield(0): devolve o controle ao
escalonador para que outra tarefa pronta possa rodar. Em laços longos, inserir
tick() é obrigatório para não monopolizar a CPU.
Duas tarefas spawn alternando com tick(); produtor/consumidor via fila compartilhada.
// Feature: spawn + tick — parallel cooperative tasks
// Syntax: `spawn { ... }` or `spawn fn_call(...)`; `tick()` yields control.
// When to use: single-threaded cooperative parallelism for
// simulations, games, animations, lightweight agents.
//
// The internal scheduler drains each task. `tick()` is equivalent to
// `coroutine.yield(0)` — hands control back to the scheduler so it
// picks another ready task. Without `tick()`, a task hogs the CPU.
fn counter(id: int, max: int) {
var n = 0
while n < max {
print("counter {id}: {n}")
n += 1
tick()
}
}
print("=== spawn + named function ===")
spawn counter(1, 3)
spawn counter(2, 3)
// `spawn { ... }` — anonymous block, direct.
print("=== spawn block ===")
spawn {
var i = 0
while i < 3 {
print("block A: {i}")
i += 1
tick()
}
}
spawn {
var i = 0
while i < 3 {
print("block B: {i}")
i += 1
tick()
}
}
// Simple producer/consumer with a shared queue.
let queue: [int] = []
var produced = 0
var consumed = 0
spawn {
var i = 0
while i < 5 {
queue.push(i)
produced += 1
tick()
i += 1
}
}
spawn {
while consumed < 5 {
if queue.len() > 0 {
let v = queue.shift()
consumed += 1
print("consumed: {v}")
}
tick()
}
}
// The scheduler drains automatically once the top-level finishes.
Para programar trabalho no tempo, use every <duração> { ... } (laço
periódico) e after <duração> { ... } (execução única diferida). Ambos são
açúcar para spawn + loop + tick(<dur>) e podem coexistir em paralelo.
Game loop com every 50ms, disparo único com after, e dois every paralelos com ritmos diferentes.
// Feature: every / after — cooperative temporal loops
// Syntax: `every <duration> { body }`, `after <duration> { body }`
// When to use: games (game loop), animations, polling, lightweight
// scheduling without needing OS threads.
//
// `every` is sugar for `spawn + loop + tick(<dur>)`. `after` schedules
// the body to run a single time after the duration.
// Simple game loop: tick every 50ms until 5 frames.
var frame = 0
every 50ms {
frame += 1
print("frame {frame}")
if frame >= 5 {
break
}
}
// `after` — single deferred execution.
print("before after")
after 10ms {
print("this runs 10ms later")
}
// `every` without a duration = on every tick (most frequent).
var i = 0
every {
i += 1
print("fast tick {i}")
if i >= 3 {
break
}
}
// Combining: two `every` loops in parallel.
var a = 0
var b = 0
every 30ms {
a += 1
print("A {a}")
if a >= 3 { break }
}
every 50ms {
b += 1
print("B {b}")
if b >= 3 { break }
}
Como o modelo é cooperativo, não há task.kill(). O cancelamento é
feito por uma flag compartilhada que a tarefa verifica a cada tick(). Esse
padrão garante que o estado nunca seja corrompido a meio caminho.
Cancelamento por flag e padrão de prazo com after sinalizando um sentinel.
// Feature: cooperative cancellation of tasks
// Syntax: shared flag/sentinel + check on every `tick()`.
// When to use: long tasks that need to stop when the user
// cancels, the window closes, or a deadline is reached.
//
// Since spawn is cooperative, cancellation is too: the task must
// ASK whether it was canceled. There is no forced kill — that is a
// feature, not a bug (avoids corrupted state).
let cancel = #{requested: false}
fn long_task(id: int) {
var step = 0
while step < 1000 {
if cancel.requested {
print("task {id} canceled at step {step}")
return
}
if step % 3 == 0 {
print("task {id}: step {step}")
}
step += 1
tick()
}
print("task {id} finished normally")
}
spawn long_task(1)
spawn long_task(2)
// After a few iterations, signal cancellation.
var cycles = 0
every 10ms {
cycles += 1
if cycles >= 3 {
cancel.requested = true
print(">> cancellation requested")
break
}
}
// Deadline pattern: cancel after X ms.
let deadline = #{expired: false}
spawn {
var n = 0
while n < 1000 {
if deadline.expired {
print("deadline reached after {n} steps")
return
}
n += 1
tick()
}
}
after 50ms {
deadline.expired = true
}
Desafio
Adicione uma segunda flag paused ao exemplo de cancelamento. A tarefa deve
parar de imprimir enquanto paused for true, mas continuar ao ser reativada.