C# 您可能会看到什么<;T>;。ReadAny实现看起来像什么?
C# 您可能会看到什么<;T>;。ReadAny实现看起来像什么?,c#,task-parallel-library,system.threading.channels,C#,Task Parallel Library,System.threading.channels,BlockingCollection有一个方便的方法,允许您使用多个集合“我想要这些集合中的下一个项目” ChannelReader没有等价物,因此,如果您确实想将多个频道消耗到一个流中,比如说将接收到的项目逐个打印到控制台,该如何实现?快速路径很容易,但慢速路径相当棘手。下面的实现返回一个数组,其中包含从其中一个读卡器获取的值,以及输入数组中该读卡器的从零开始的索引 public static Task<(T Item, int Index)> ReadFromAnyAsync&l
BlockingCollection
有一个方便的方法,允许您使用多个集合“我想要这些集合中的下一个项目”
ChannelReader
没有等价物,因此,如果您确实想将多个频道消耗到一个流中,比如说将接收到的项目逐个打印到控制台,该如何实现?快速路径很容易,但慢速路径相当棘手。下面的实现返回一个数组,其中包含从其中一个读卡器获取的值,以及输入数组中该读卡器的从零开始的索引
public static Task<(T Item, int Index)> ReadFromAnyAsync<T>(
params ChannelReader<T>[] channelReaders) =>
ReadFromAnyAsync(channelReaders, CancellationToken.None);
public static async Task<(T Item, int Index)> ReadFromAnyAsync<T>(
ChannelReader<T>[] channelReaders,
CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
// Fast path
for (int i = 0; i < channelReaders.Length; i++)
{
if (channelReaders[i].TryRead(out var item)) return (item, i);
}
// Slow path
var locker = new object();
int resultIndex = -1;
T resultItem = default;
while (true)
{
using (var cts = CancellationTokenSource
.CreateLinkedTokenSource(cancellationToken, default))
{
bool availableAny = false;
Task[] tasks = channelReaders
.Select(async (reader, index) =>
{
try
{
bool available = await reader.WaitToReadAsync(cts.Token)
.ConfigureAwait(false);
if (!available) return;
}
catch // Cancellation, or channel completed with exception
{
return;
}
availableAny = true;
lock (locker) // Take from one reader only
{
if (resultIndex == -1 && reader.TryRead(out var item))
{
resultIndex = index;
resultItem = item;
cts.Cancel();
}
}
})
.ToArray();
await Task.WhenAll(tasks).ConfigureAwait(false);
if (resultIndex != -1) return (resultItem, resultIndex);
cancellationToken.ThrowIfCancellationRequested();
if (!availableAny) throw new ChannelClosedException(
"All channels are marked as completed.");
}
}
}
公共静态任务ReadFromAnyAsync(
参数ChannelReader[]channelReaders)=>
ReadFromAnyAsync(channelReaders,CancellationToken.None);
公共静态异步任务ReadFromAnyAsync(
频道阅读器[]频道阅读器,
取消令牌(取消令牌)
{
cancellationToken.ThrowIfCancellationRequested();
//快车道
for(int i=0;i
{
尝试
{
bool available=wait reader.WaitToReadAsync(cts.Token)
.配置等待(错误);
如果(!可用)返回;
}
捕获//取消,或通道已完成,但出现异常
{
返回;
}
有效性=真;
锁(储物柜)//仅从一个读卡器中取出
{
if(resultIndex==-1&&reader.TryRead(out变量项))
{
结果指数=指数;
结果m=项目;
cts.Cancel();
}
}
})
.ToArray();
等待任务.WhenAll(任务).配置等待(false);
if(resultIndex!=-1)返回(resultItem,resultIndex);
cancellationToken.ThrowIfCancellationRequested();
如果(!availableAny)抛出新的ChannelClosedException(
“所有通道均标记为已完成。”);
}
}
}
快速路径很容易,但慢速路径相当棘手。下面的实现返回一个数组,其中包含从其中一个读卡器获取的值,以及输入数组中该读卡器的从零开始的索引
public static Task<(T Item, int Index)> ReadFromAnyAsync<T>(
params ChannelReader<T>[] channelReaders) =>
ReadFromAnyAsync(channelReaders, CancellationToken.None);
public static async Task<(T Item, int Index)> ReadFromAnyAsync<T>(
ChannelReader<T>[] channelReaders,
CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
// Fast path
for (int i = 0; i < channelReaders.Length; i++)
{
if (channelReaders[i].TryRead(out var item)) return (item, i);
}
// Slow path
var locker = new object();
int resultIndex = -1;
T resultItem = default;
while (true)
{
using (var cts = CancellationTokenSource
.CreateLinkedTokenSource(cancellationToken, default))
{
bool availableAny = false;
Task[] tasks = channelReaders
.Select(async (reader, index) =>
{
try
{
bool available = await reader.WaitToReadAsync(cts.Token)
.ConfigureAwait(false);
if (!available) return;
}
catch // Cancellation, or channel completed with exception
{
return;
}
availableAny = true;
lock (locker) // Take from one reader only
{
if (resultIndex == -1 && reader.TryRead(out var item))
{
resultIndex = index;
resultItem = item;
cts.Cancel();
}
}
})
.ToArray();
await Task.WhenAll(tasks).ConfigureAwait(false);
if (resultIndex != -1) return (resultItem, resultIndex);
cancellationToken.ThrowIfCancellationRequested();
if (!availableAny) throw new ChannelClosedException(
"All channels are marked as completed.");
}
}
}
公共静态任务ReadFromAnyAsync(
参数ChannelReader[]channelReaders)=>
ReadFromAnyAsync(channelReaders,CancellationToken.None);
公共静态异步任务ReadFromAnyAsync(
频道阅读器[]频道阅读器,
取消令牌(取消令牌)
{
cancellationToken.ThrowIfCancellationRequested();
//快车道
for(int i=0;i
{
尝试
{
bool available=wait reader.WaitToReadAsync(cts.Token)
.配置等待(错误);
如果(!可用)返回;
}
捕获//取消,或通道已完成,但出现异常
{
返回;
}
有效性=真;
锁(储物柜)//仅从一个读卡器中取出
{
if(resultIndex==-1&&reader.TryRead(out变量项))
{
结果指数=指数;
结果m=项目;
cts.Cancel();
}
}
})
.ToArray();
等待任务.WhenAll(任务).配置等待(false);
if(resultIndex!=-1)返回(resultItem,resultIndex);
cancellationToken.ThrowIfCancellationRequested();
如果(!availableAny)抛出新的ChannelClosedException(
“所有通道均标记为已完成。”);
}
}
}
也许你可以从多个制作人(如频道)向单个BlockingCollection
中添加项目,并使用单个循环消费。我想用频道
来取代BlockingCollection
,但相同的想法是,多个频道阅读器
为另一个频道编写器
,然后被另一个通道读取器
使用,可以工作。不过它看起来很笨重。也许你可以从多个制作人(如频道)向单个BlockingCollection
添加项目,并通过单个循环使用它。我会寻求使用Channel
来取代BlockingCollection
,但同样的想法,多个频道阅读器
向另一个频道阅读器
提供信息,然后被另一个频道阅读器
使用,这些阅读器可以工作。不过它看起来很笨重。