nim 提供了 spawn 簡化工作在 thread 間的分派, 例子如下

import os, threadpool

proc task(): string =
    sleep(3 * 1000)
    result = "hello"

var v = spawn task()

echo "Wait for task..."
echo ^v
  • spawn 是由 threadpoll module 提供
  • spawn 後是 proc 的呼叫
  • 傳回的是 FlowVar[T] (或是 void) 的類型
  • 在 task 結束後, v (type 為 FlowVar[string]) 會被放入 "hellow"
  • 可用 ^ operator 對 v 進行等待

Thread 間要通訊的話, 可用 Channel[T]

import os, threadpool

var c: Channel[int]
c.open()

proc task(): string =
    echo "Receiving delay ms..."
    let delay = c.recv()
    echo "Go delay ms: ", delay
    sleep(delay)
    result = "hello"

var v = spawn task()

echo "Delay 3000ms then order"
sleep(3000)
c.send(3000)

echo "Wait for task..."
echo ^v

因為 spawn 呼叫的 proc 不能有 var 類型的 argument (send()recv() 時用的參數類型為 var 的, 所以傳入非 var 給這裡的 task() 並沒有什麼用處), 所以這裡是通過 global variable 共享 c (type 為 Channel[int]), 並用 send()recv() 送收訊息.

如果, 有多個 spawn 出去的 task 需要等待, 可用 sync() 完成 - 當然, 會 block 目前的 thread.

雖說 spawn 呼叫的 proc 為了 thread safty 不能有 var argument, 但可用 ptr 做為 workaround

import os, threadpool

proc task(c: ptr Channel[int]): string =
    echo "Receiving delay ms..."
    let delay = c[].recv()
    echo "Go delay ms: ", delay
    sleep(delay)
    result = "hello"

var c: Channel[int]
c.open()
var v = spawn task(addr c)

echo "Delay 3000ms then order"
sleep(3000)
c.send(3000)

echo "Wait for task..."
echo ^v

要注意的是

  • Channel[T] 是 thread safe 的, 所以通過 ptr argument 共享沒升麼問題
  • 因為傳入 task() 的 channel 是 ptr 類型的, 所以需要配合 [] 進行 dereference
  • nim 不會追蹤 ptr 的生命週期, 所以要確保被共享的 Channel[T] 在使用期間不被 GC 回收
  • 要不, 就得配合 createShared() 將 channel 配置在 shared heap 上, 這樣才能被任意的 thread freeShared()