今回は、System.Threading.Channels を Unity に導入した実装例です。 次の記事を参考にさせて頂きました。
前回の記事もご参考までに。
Unity - UniTask の Channel で生産者/消費者パターンを利用する - yotiky Tech Blog
検証環境
以下の環境で、Unity Editor 上で実行しています。
- Unity 2019.3.15f1
- Script Backend : Mono
- Api Compability Lebel : .NET 4.x
- System.Threading.Channels 4.7.1
- system.threading.tasks.extensions.4.5.4
- system.runtime.compilerservices.unsafe.4.7.1
目次
導入
まずは System.Threading.Channels を Unity プロジェクトに導入します。 以下のサイトより package をダウンロードし、zip ファイルにリネームして解凍します。System.Threading.Channels.dll を Unity プロジェクトにD&Dで。 Dependencies なライブラリがあるの続く2つのパッケージも同様にして、Unity に入れればOKです。
NuGet Gallery | System.Threading.Tasks.Extensions 4.5.4
NuGet Gallery | System.Runtime.CompilerServices.Unsafe 4.7.1
実装例
生産者 1 : 消費者 1
1対1の実装例です。ほとんど参考にさせて頂いた記事のコードのままです。
async Task Single() { var channel = Channel.CreateUnbounded<int>( new UnboundedChannelOptions { SingleReader = true, SingleWriter = true, }); var consumer = Task.Run(async () => { while (await channel.Reader.WaitToReadAsync()) { Debug.Log(await channel.Reader.ReadAsync()); } }); var producer = Task.Run(async () => { await channel.Writer.WriteAsync(1); await channel.Writer.WriteAsync(2); await channel.Writer.WriteAsync(3); channel.Writer.Complete(); }); await Task.WhenAll(consumer, producer); Debug.Log("Completed."); }
実行結果です。
1 2 3
生産者 n : 消費者 1
n対1の実装例です。
async Task MultiToSingle() { var channel = Channel.CreateUnbounded<int>( new UnboundedChannelOptions { SingleReader = true, }); var consumer = Task.Run(async () => { while (await channel.Reader.WaitToReadAsync()) { Debug.Log("Producer" + await channel.Reader.ReadAsync()); } }); var producers = Enumerable.Range(1, 3) .Select(producerId => Task.Run(async () => { await channel.Writer.WriteAsync(producerId); })); await Task.WhenAll(producers); channel.Writer.Complete(); await consumer; Debug.Log("Completed."); }
実行結果です。
Producer1 Producer2 Producer3
生産者 1 : 消費者 n
最後に 1対nの実装例です。先に説明したとおり、複数の消費者はキューを消費するわけではなく、等しく購読するので注意が必要です。
async Task SingleToMulti() { var channel = Channel.CreateUnbounded<int>( new UnboundedChannelOptions { SingleWriter = true, }); var consumers = Enumerable.Range(1, 3) .Select(consumerId => Task.Run(async () => { while (await channel.Reader.WaitToReadAsync()) { if (channel.Reader.TryRead(out var value)) { Debug.Log($"Consumer{consumerId}:{value}"); } } })); var producer = Task.Run(async () => { await channel.Writer.WriteAsync(1); await channel.Writer.WriteAsync(2); await channel.Writer.WriteAsync(3); channel.Writer.Complete(); }); await Task.WhenAll(consumers.Union(new[] { producer })); Debug.Log("Completed."); }
実行結果です。
Consumer2:3 Consumer1:1 Consumer3:2