跳到主要内容

异步系统

Alia 支持原生并行处理、线程控制与异步编程。核心概念包括 run/await(线程启停)、Channel(线程间通信)和 select(多路选择)。

语法总览​

run <Stack body> ~ 在独立线程中执行栈,返回附带线程状态的 Stack
run <Variable stackVar> ~ 启动已有的 Stack 对象
await <Stack stackVal> ~ 等待并行栈完成,返回执行结果;非并行栈报 Stack is not running in parallel
new Channel[<Number cap>] ~ 创建有缓冲信道
new Channel[] ~ 创建无缓冲信道
<Channel>.send <Value> ~ 发送值;已关闭或有缓冲已满时抛错
<Channel>.trySend <Value> ~ 非阻塞发送,返回 Boolean
<Channel>.receive ~ 接收值;有缓冲为空时返回 Null,无缓冲为空时阻塞
<Channel>.tryReceive ~ 非阻塞接收,空时返回 Null
<Channel>.close ~ 关闭信道
select <Stack body> ~ 多路选择,body 中仅允许 case receive / case send 子句
case receive <Variable> from <Value> <Stack body> ~ 接收分支
case send <Value> to <Value> <Stack body> ~ 发送分支

零参数信道与栈方法采用 call 形式:call ch.receive、call ch.tryReceive、call ch.close、call t.join、call t.result、call t.detach、call s.run。属性采用直接访问形式:ch.capacity、ch.length、ch.isClosed、t.isRunning、t.isDone、t.isDetached。

Channel 信道​

Channel 是 Alia 内置的线程安全通信信道,对标 Go 语言的 channel。

创建信道​

const ch = new Channel[5]
const uch = new Channel[]
assert ch.capacity == 5 error 'buffered capacity mismatch'
assert uch.capacity == 0 error 'unbuffered capacity mismatch'

基本操作​

const ch = new Channel[3]
ch.send 'hello'
ch.send 42
assert ch.length == 2 error 'length mismatch after send'
assert (call ch.receive) == 'hello' error 'receive mismatch'
assert (call ch.receive) == 42 error 'receive number mismatch'
assert ch.length == 0 error 'length mismatch after receive'

非阻塞操作在缓冲未满或非空时成功,条件不满足时返回 false 或 Null,不抛错:

const ch = new Channel[1]
assert ch.trySend 'x' error 'trySend mismatch'
assert (ch.trySend 'y') == false error 'trySend full mismatch'
assert (call ch.tryReceive) == 'x' error 'tryReceive mismatch'
assert (typeOf call ch.tryReceive) == 'Null' error 'empty tryReceive mismatch'

关闭操作将信道标记为已关闭,已缓冲的值仍可接收,向已关闭信道发送时抛错:

const ch = new Channel[2]
ch.send 'a'
call ch.close
assert ch.isClosed error 'should be closed'
assert (call ch.receive) == 'a' error 'buffered receive after close mismatch'

有缓冲 send 在缓冲区已满时抛 Channel buffer is full,receive 在缓冲区为空时返回 Null。无缓冲 receive 在缓冲区为空且未关闭时阻塞等待,close 唤醒等待者。

属性​

属性类型说明
capacityNumber缓冲区容量(无缓冲=0)
lengthNumber当前缓冲区元素数
isClosedBoolean是否已关闭

属性为直接访问形式,不经 call。

线程(run / await)​

启动线程​

run 将 Stack 放入独立线程并行执行,返回附带线程状态的同一 Stack 对象:

const t = run {
print 'Hello from thread!'
42
}
assert t.isRunning error 'thread should be running'

工作线程基于栈源码重建独立运行时执行,不共享创建线程的变量与信道缓冲。需要传递结果的场景由 await 或 call t.join 在主线程收集。

等待线程​

await 阻塞当前线程,等待目标线程完成并返回执行结果:

const t = run { 42 }
const v = await t
assert v == 42 error 'await result mismatch'
assert t.isDone error 'should be done'

await 的目标须为处于并行执行中的 Stack。目标非并行栈时报 Stack is not running in parallel。重复启动已运行的栈报 Stack is already running in parallel。

const s = { 42 }
assert s.isRunning == false error 'fresh stack should not be running'
assert s.isDone == false error 'fresh stack should not be done'

线程属性​

属性/方法类型说明
isRunningBoolean是否正在并行执行,直接访问
isDoneBoolean是否已执行完毕,直接访问
isDetachedBoolean是否已分离,直接访问
join→ Value等待完成并返回结果(阻塞),调用形式为 call t.join
detach→ Null分离线程,标记 isDetached 并返回 Null,调用形式为 call t.detach
result→ Value?非阻塞获取结果,未完成返回 Null,调用形式为 call t.result
const t = run { 40 + 2 }
assert (call t.join) == 42 error 'join result mismatch'
assert t.isDone error 'should be done after join'
const t = run { 42 }
call t.detach
assert t.isDetached error 'should be detached'
await t

select 多路选择​

select 语句在多条信道操作中等待,执行第一个就绪的分支。分支就绪判定基于非阻塞 tryReceive/trySend:

const ch1 = new Channel[1]
const ch2 = new Channel[1]
ch1.send 'A'
ch2.send 'B'
let received = ''
select {
case receive v1 from ch1 {
received = v1
}
case receive v2 from ch2 {
received = v2
}
}
assert received == 'A' | received == 'B' error 'select receive mismatch'

当没有信道就绪时,select 不执行任何分支:

const ch = new Channel[1]
let ran = false
select {
case receive v from ch {
ran = true
}
}
assert ran == false error 'select empty mismatch'

发送分支在目标信道可写入时就绪:

const ch1 = new Channel[1]
const ch2 = new Channel[1]
ch1.send 'A'
let out = ''
select {
case receive v from ch1 {
out = v
}
case send 'B' to ch2 {
out = 'sent'
}
}
assert out == 'A' error 'select send branch mismatch'

完整示例:Worker Pool​

并行计算在独立线程中执行,信道在主线程中完成任务分发与结果收集。run 体不捕获外部信道,跨线程结果经 await 回到主线程后再入信道:

const t1 = run { 1 + 2 }
const t2 = run { 3 * 4 }
const v1 = await t1
const v2 = await t2
const results = new Channel[10]
results.send v1
results.send v2
call results.close
assert results.length == 2 error 'results length mismatch'
assert (call results.tryReceive) == 3 error 'first result mismatch'
assert (call results.tryReceive) == 12 error 'second result mismatch'

信道任务流水线在主线程中顺序执行:

const tasks = new Channel[10]
const orderedResults = new Channel[10]
each i in [1, 2, 3, 4, 5] {
tasks.send i
}
call tasks.close
while tasks.length > 0 {
const item = call tasks.tryReceive
if typeOf item == 'Number' {
const doubled = item * 2
orderedResults.send doubled
}
}
call orderedResults.close
assert orderedResults.length == 5 error 'ordered results mismatch'

标准库异步函数​

部分标准库函数返回符合 Alia 异步模型的 Stack(isRunning/isDone/isDetached 属性与 join/result/detach 方法,经 call 调用),经 await 消费:

  • io.read / io.readChar / io.readKey:属性式访问返回 Stack,call task.join 惰性执行同步读取,EOF 返回 Null
  • io.readLine / io.readPassword:单参数调用返回 Stack,io.readLine 'prompt' 传入提示词,EOF 返回 Null
  • web.httpRequest:单参数对象调用,请求在后台线程执行,await 后经 task.response 访问响应对象,await 表达式可一步取得响应

await 接受返回 Stack 的表达式,一步取得结果;栈字面量、并行栈与上述异步 Stack 的 typeOf 均为 'Stack'。web.httpRequest 的 await 语义与测试一致:const task = web.httpRequest opts 后 await task 经 task.response 访问响应对象,await web.httpRequest opts 一步取得响应;以下示例仅创建任务并校验 Stack 形态,不发起等待,避免示例依赖外部服务:

import 'web' as web
const opts = *[url: 'http://localhost:19995/get']
const task = web.httpRequest opts
assert task.isRunning error 'http task should be running'
assert (typeOf task) == 'Stack' error 'http task type mismatch'
import 'web' as web
const parsed = web.urlParse 'https://localhost/path'
assert parsed.protocol == 'https' error 'protocol mismatch'
assert parsed.hostname == 'localhost' error 'hostname mismatch'
import 'web' as web
const server = call web.serverCreate
server.close
assert true error 'server close mismatch'

web.serverCreate 经 call 创建服务对象,server.close 为属性式访问,访问时同步执行关闭并返回已完成的 Stack。server.listen 与 addRoute 为普通服务方法。

下一步​