我有两个使用SemaphoreSlim的循环和一个字符串“ Contents”的数组
一个foreachloop:
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(allTasks)返回一个OutOfRangeException,我希望Contents []索引和List索引匹配。
我可以修复for循环吗?还是有更好的方法?
答案 0 :(得分:2)
这将解决您当前的问题
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
}
还有许多其他方法可以执行此操作,PLinq
,Parallel.For
,Parallel.ForEach
,或者只是像上面那样在循环中整理捕获的内容。
但是,由于您有IO约束的工作负载,并且您有async
个方法来运行它。最合适的解决方案是async
,await
都无法最佳地满足需求的Parallel.For
Parallel.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
}
}
基本上,这会创建一个BufferBlock
和TransformBlock
,您将工作负载泵入TransformBlock
,它的选项具有并行度,并将它们推入{{1 }},您等待完成并获得结果。
为什么要进行数据流处理?因为它处理BufferBlock
async
,所以它具有await
,它是为IO绑定或CPU绑定的工作负载而设计的,并且非常易于使用。此外,由于通常以多种方式(在管道中)处理大多数数据,因此您可以使用它来按顺序,并行或以任何方式选择管道来管道化和操纵数据流。
无论如何,祝你好运