gpt4 book ai didi

c# - Task.Whenall(List) 输出 OutOfRangeException

转载 作者:行者123 更新时间:2023-11-30 22:58:37 31 4
gpt4 key购买 nike

我有两个使用 SemaphoreSlim 和字符串数组“Contents”的循环

一个foreach循环:

        var allTasks = new List<Task>();
var throttle = new SemaphoreSlim(10,10);
foreach (string s in Contents)
{
await throttle.WaitAsync();
allTasks.Add(
Task.Run(async () =>
{
try
{
rootResponse.Add(await POSTAsync(s, siteurl, src, target));
}
finally
{
throttle.Release();
}
}));
}
await Task.WhenAll(allTasks);

一个for循环:

        var allTasks = new List<Task>();
var throttle = new SemaphoreSlim(10,10);
for(int s=0;s<Contents.Count;s++)
{
await throttle.WaitAsync();
allTasks.Add(
Task.Run(async () =>
{
try
{
rootResponse[s] = await POSTAsync(Contents[s], siteurl, src, target);
}
finally
{
throttle.Release();
}
}));
}
await Task.WhenAll(allTasks);

第一个 foreach 循环运行良好,但 for 循环 Task.WhenAll(allTask​​s) 返回 OutOfRangeException,我希望 Contents[] 索引和 List 索引匹配。

我可以修复 for 循环吗?还是有更好的方法?

最佳答案

这将解决您当前的问题

for (int s = 0; s < Contents.Count; s++)
{
var content = Contents[s];

allTasks.Add(
Task.Run(async () =>
{
await throttle.WaitAsync();
try
{
rootResponse[s] = await POSTAsync(content, siteurl, src, target);
}
finally
{
throttle.Release();
}
}));
}
await Task.WhenAll(allTasks);

然而,这是一段相当困惑和讨厌的代码。这看起来有点整洁

public static async Task DoStuffAsync(Content[] contents, string siteurl, string src, string target)
{
var throttle = new SemaphoreSlim(10, 10);

// local method
async Task<(Content, SomeResponse)> PostAsyncWrapper(Content content)
{
await throttle.WaitAsync();
try
{
// return a content and result pair
return (content, await PostAsync(content, siteurl, src, target));
}
finally
{
throttle.Release();
}
}

var results = await Task.WhenAll(contents.Select(PostAsyncWrapper));

// do stuff with your results pairs here
}

还有许多其他方法可以做到这一点,PLinqParallel.ForParallel.ForEach,或者只是整理您的捕获在你上面的循环中。

然而,由于您有一个 IO 绑定(bind)工作负载,并且您有运行它的 async 方法。最合适的解决方案是 async await 模式,Parallel.ForParallel.ForEach 都无法满足最佳需求。

另一种方式是TPL DataFlow可以在 System.Threading.Tasks.Dataflow nuget 中找到的库包。

代码

public static async Task DoStuffAsync(Content[] contents, string siteurl, string src, string target)
{

async Task<(Content, SomeResponse)> PostAsyncWrapper(Content content)
{
return (content, await PostAsync(content, siteurl, src, target));
}

var bufferblock = new BufferBlock<(Content, SomeResponse)>();
var actionBlock = new TransformBlock<Content, (Content, SomeResponse)>(
content => PostAsyncWrapper(content),
new ExecutionDataflowBlockOptions
{
EnsureOrdered = false,
MaxDegreeOfParallelism = 100,
SingleProducerConstrained = true
});
actionBlock.LinkTo(bufferblock);

foreach (var content in contents)
actionBlock.Post(content);

actionBlock.Complete();
await actionBlock.Completion;

if (bufferblock.TryReceiveAll(out var result))
{
// do stuff with your results pairs here
}

}

基本上这会创建一个 BufferBlockTransformBlock,您将工作负载注入(inject) TransformBlock,它的选项具有并行度,并将它们插入 BufferBlock,您等待完成并获得结果。

为什么选择数据流?因为它处理 async await,它具有 MaxDegreeOfParallelism,它专为 IO bound 或 CPU Bound 工作负载而设计,并且使用起来非常简单。此外,由于大多数数据通常以多种方式(在管道中)进行处理,因此您可以使用它按顺序和并行方式或以您选择的任何方式来管道和操作数据流。

祝你好运

关于c# - Task.Whenall(List<Task>) 输出 OutOfRangeException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52911847/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com