チャネルとコルーチン
チャネルは並行タスク間で値を交換します。バッファ付きまたはバッファなしで作成でき、channel.select と組み合わせて複数の操作を調整できます。
このページはAPIリファレンスです。基本的なブロックは独立したスニペットです。タイムアウト、ファンイン、ノンブロッキングの節は、名前付きチャネルとコールバックを周囲のアプリケーションから受け取る部分的なパターンです。ワーカープールのブロックは、プロセス内で完結する例です。
channel と coroutine のグローバルは常に利用できます。チャネルは1つのLuaプロセス内のコルーチンを調整します。プロセス境界をまたぐ場合は、プロセスメッセージ、関数、またはキューを使用してください。
チャネルの作成
バッファなしチャネル(サイズ0)では、転送を完了するために送信側と受信側の両方が準備できている必要があります。バッファ付きチャネルでは、バッファに空きがある間は送信を完了できます。
-- Unbuffered: synchronizes sender and receiver
local sync_ch = channel.new()
-- Buffered: queue up to 10 messages
local work_queue = channel.new(10)
| パラメータ | 型 | 説明 |
|---|---|---|
size |
integer | バッファ容量(デフォルト:0でアンバッファード) |
戻り値: channel
値の送信
送信は、バッファなしチャネルでは受信側の準備ができるまで、バッファ付きチャネルではバッファに空きができるまでブロックします。
-- Send work to a worker pool
local tasks = {"task-a", "task-b"}
local jobs = channel.new(100)
for i, task in ipairs(tasks) do
jobs:send(task) -- Blocks if buffer full
end
jobs:close() -- Signal no more work
| パラメータ | 型 | 説明 |
|---|---|---|
value |
any | 送信する値 |
戻り値: boolean
クローズ済みのチャネルへの送信はエラーになります。
値の受信
受信は、値が利用可能になるか、チャネルがクローズされるまでブロックします。
-- Worker consuming from job queue
while true do
local job, ok = jobs:receive()
if not ok then
break -- Channel closed, no more work
end
process(job)
end
ここでは、jobs はアプリケーションから提供されるキューであり、process はタスクを処理するコールバックです。
戻り値: any, boolean
value, true— 値を受信したnil, false— チャネルがクローズされ、空である
チャネルのクローズ
チャネルをクローズすると、待機中の送信側はエラーを受け取り、待機中の受信側は nil, false を受け取ります。すでにクローズ済みのチャネルを閉じても何も起こりません。
local results = channel.new(10)
-- Producer fills results
for _, item in ipairs(data) do
results:send(process(item))
end
results:close() -- Signal completion
この独立したproducerスニペットでは、data と process コールバックがアプリケーションから提供されるものとします。
複数チャネルからのSelect
channel.select は複数のチャネル操作を同時に待機します。イベントソース、タイムアウト、ノンブロッキングチェックを調整できます。
local result = channel.select(cases)
| パラメータ | 型 | 説明 |
|---|---|---|
cases |
table | selectケースの配列 |
default |
boolean | trueなら、ケースが準備できていない場合即座に戻る |
戻り値: table
- チャネルケースの場合:
{channel, value, ok}—channelはそのケースのチャネル、valueは受信/送信された値、okはクローズ済みチャネルからの受信で false になります。 - デフォルト分岐の場合(準備できているケースがなく
default = trueのとき):{default = true, ok = true}。
タイムアウトパターン
time.after() を使用してチャネル待機にタイムアウトを追加します。
local time = require("time")
local result_ch = application_response_channel
local timeout, err = time.after("5s")
if err then
return nil, err
end
local r = channel.select {
result_ch:case_receive(),
timeout:case_receive()
}
if r.channel == timeout then
return nil, errors.new({ kind = errors.TIMEOUT, message = "Operation timed out" })
end
return r.value
この部分的なパターンでは、エントリの modules: に time が含まれ、application_response_channel がアプリケーションから提供されるものとします。time.after は成功時にチャネルを1つ返します。無効または正でない期間の場合は nil, error を返します。
ファンインパターン
複数のソースからの値を1つのループで処理します。
このprocessエントリのパターンはグローバルな process を使用し、シャットダウン信号と2つのハンドラ関数はアプリケーションから提供されます。
local events = process.events()
local inbox = process.inbox()
local shutdown = channel.new()
while true do
local r = channel.select {
events:case_receive(),
inbox:case_receive(),
shutdown:case_receive()
}
if r.channel == shutdown then
break
elseif r.channel == events then
handle_event(r.value)
else
handle_message(r.value)
end
end
ノンブロッキングチェック
デフォルトケースを使用して、利用可能なデータをブロックせずに確認します。
この独立したパターンでは、ch と process コールバックがアプリケーションから提供されます。
local r = channel.select {
ch:case_receive(),
default = true
}
if r.default then
-- Nothing available, do something else
elseif not r.ok then
-- The channel is closed
else
process(r.value)
end
Selectケースの作成
channel.select の送信ケースと受信ケースを作成します。
-- Send case - completes when channel can accept value
ch:case_send(value)
-- Receive case - completes when value available
ch:case_receive()
casesテーブル内の、送信ケースでも受信ケースでもない値は無視されます。デフォルト分岐もない場合は、テーブルに少なくとも1つの有効なケースが含まれるようにしてください。
ワーカープールパターン
local items = {1, 2, 3, 4}
local num_workers = 2
local function process_item(item)
return item * 2
end
local work = channel.new(#items)
local results = channel.new(#items)
-- Spawn workers
for _ = 1, num_workers do
coroutine.spawn(function()
while true do
local item, ok = work:receive()
if not ok then
return
end
results:send(process_item(item))
end
end)
end
-- Feed work
for _, item in ipairs(items) do
work:send(item)
end
work:close()
-- Collect results
local processed = {}
while #processed < #items do
local result, ok = results:receive()
if not ok then break end
table.insert(processed, result)
end
ループ終了後、processed には 2、4、6、8 が含まれます。結果の順序はコルーチンのスケジューリングによって異なります。ワーカーは同じLuaプロセス内のコルーチンなので、チャネルを共有します。
エラー
| 条件 | 種別 | 再試行可能 |
|---|---|---|
| クローズされたチャネルへの送信 | runtime error | no |
selectのcases引数がテーブルでない |
runtime error | no |