Unity - System.Threading.Channels で生産者/消費者パターンを利用する
今回は、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