Pular para o conteúdo

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.

04-spawn-tick.zolo
Playground
// 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.

05-every-after.zolo
Playground
// 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.

08-cancellation.zolo
Playground
// 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.

Buscar no Zolo

9 resultados

enespt-br