C#向けに async/await 対応の待機可能なコルーチンライブラリ AwaitableCoroutine を作ったから紹介
コルーチンについて
コルーチンはいったん処理を中断した後、続きから処理を再開できる。コルーチン - Wikipedia
ゲーム開発してると欲しくなることが多いですね。
C#では IEnumerator
を利用して記述することが多いと思いますが、微妙な点として、次のような事が挙げられます。
-
IEnumerator
は遅延シーケンスのための機能であり、コルーチンを管理する仕組みは別途必要。 - 必ずメソッドとして定義する必要がある。
- 子のコルーチンを実行する際に、一々
foreach
で記述する必要がある。- あるいは、管理する仕組みの方で頑張る
一方で Task
を使おうとすると、マルチスレッドのための余計なオーバーヘッドがあったり、一時停止やキャンセルは少し面倒だったりします。
参考
AwaitableCoroutine
using System;
using AwaitableCoroutine;
// コルーチンを管理・実行するオブジェクト
var runner = new CoroutineRunner();
int count = 0;
// コルーチンを作成・登録する
var coroutine = runner.Create(async () => {
Console.WriteLine("Started!");
for (var i = 0; i < 10; i++)
{
count++;
await Coroutine.Yield();
}
}).OnCompleted(() => Console.WriteLine("Finished!"));
while (true)
{
// runnerに登録されたコルーチンの処理を進める。
runner.Update();
// コルーチンが正常に完了していたらループを抜ける
if (coroutine.IsCompletedSuccessfully) break;
Console.WriteLine($"{count}");
}
-
CoroutineRunner
のUpdate
メソッドを呼び出すことで(明示的に)処理を進める。 - コルーチンのキャンセルも簡単にできる。
- 子のコルーチンは、コルーチンを呼び出して
await
するだけ。 - asyncラムダ式でコルーチンを記述できる。
- もちろん、通常のasyncメソッドでも記述できる。
嬉しいですね。
補足
async/await
で Task
みたいな見た目ですが、AwaitableCoroutine
はシングルスレッドのための非同期処理であって、マルチスレッドは絡まないです。 コルーチンの処理は CoroutineRunner.Update
したときに実行されます。
注意点
Coroutine
を作成する際にどの ICoroutineRunner
に登録するか教えてあげる必要があります。呼び出し元で指定すれば、async
メソッド内では自動的に伝播されます。
基本的にはCreate
拡張メソッドを使用します。
private static async Coroutine FooCoroutine()
{
// 伝播されるのでここで`Create`は必要ない
await Coroutine.DelayCount(5);
Console.WriteLine("Hello!");
}
var coroutine = runner.Create(FooCoroutine);
Coroutine
のタプルを返すなど、Coroutine
またはCoroutine<T>
以外の型を作成したい場合は、以下のようにContext
拡張メソッドを利用します。
var (c1, c2) = runner.Context(() => {
return (Coroutine.DelayCount(2), Coroutine.DelayCount(3));
);
コルーチンの作成に引数を与えたい場合は以下のようにします。
var coroutine = runner.Context(() => FooBarCoroutine(arg1, arg2));
補助メソッド
色々用意しています。
- Yield
- While
- DelayCount
- WaitAll
- WaitAny
- Select, SelectTo
- AndThen
- UntilCompleted
- FromEnumerator
- AwaitTask
- AwaitObservable
- AwaitObservableCompleted
詳しくはドキュメントを見てください。
AwaitableCoroutineドキュメント
その他のパッケージ
F#向けのパッケージと、ゲームエンジンAltseed2向けのパッケージを用意してあります。
AwaitableCoroutine.FSharp
F#向けのコンピューテーション式を提供しています。
let _ = runner.Create(fun () ->
coroutine {
printfn "1"
do! Coroutine.Yield()
printfn "2"
yield ()
printfn "3"
}
)
let _ =
runner.Do {
let! _ = Coroutine.DelayCount(5)
printfn "Hello"
}
()
AwaitableCoroutine.Altseed2
ゲームエンジンAltseed2向けの拡張パッケージです。
ICoroutineRunner
を実装したCoroutineNode
と、DelaySecond
コルーチンを提供しています。CoroutineNode
は登録するとOnUpdate
でコルーチンを更新します。
var coroutineNode = new CoroutineNode();
Engine.AddNode(coroutineNode);
coroutineNode
.Create(() => Altseed2Coroutine.DelaySecond(5.0f))
.OnCompleted(() => Console.WriteLine("Finished!"));
インストール
NuGetからインストールできます。
PackageId | Badge |
---|---|
AwaitableCoroutine | |
AwaitableCoroutine.Altseed2 | |
AwaitableCoroutine.FSharp |
実装について
Task-Like、Awaitable パターン、AsyncMethodBuilder を利用しています。
参考になりそうな記事
最後に
質問あればTwitterやGitHubで。
Discussion