编辑:由于Bulkhead策略需要用WaitAndRetry策略包装,无论如何...我倾向于将示例3作为保持并行性,节流和polly策略重试的最佳解决方案。自从我以为Parallel.ForEach用于同步操作,而Bulkhead对于异步操作会更好
我正在尝试使用polly AsyncBulkheadPolicy进行限制的同时运行多个异步任务。到目前为止,我的理解是策略方法ExecuteAsync本身不会在线程上进行调用,而是将其留给默认的TaskScheduler或在其之前的某个人进行。因此,如果我的任务以某种方式受CPU限制,则在执行任务时需要使用Parallel.ForEach或将Task.Run()与ExecuteAsync方法一起使用,以便将任务调度到后台线程。
有人可以看下面的例子,并阐明它们在并行性和线程池方面如何工作吗?
https://github.com/App-vNext/Polly/wiki/Bulkhead-操作:假定我们已经这样做,则隔壁策略不会创建它自己的线程。
async Task DoSomething(IEnumerable<object> objects);
//Example 1:
//Simple use, but then I don't have access to retry policies from polly
Parallel.ForEach(groupedObjects, (set) =>
{
var task = DoSomething(set);
task.Wait();
});
//Example 2:
//Uses default TaskScheduler which may or may not run the tasks in parallel
var parallelTasks = new List<Task>();
foreach (var set in groupedObjects)
{
var task = bulkheadPolicy.ExecuteAsync(async () => DoSomething(set));
parallelTasks.Add(task);
};
await Task.WhenAll(parallelTasks);
//Example 3:
//seems to defeat the purpose of the bulkhead since Parallel.ForEach and
//PolicyBulkheadAsync can both do throttling...just use basic RetryPolicy
//here?
Parallel.ForEach(groupedObjects, (set) =>
{
var task = bulkheadPolicy.ExecuteAsync(async () => DoSomething(set));
task.Wait();
});
//Example 4:
//Task.Run still uses the default Task scheduler and isn't any different than
//Example 2; just makes more tasks...this is my understanding.
var parallelTasks = new List<Task>();
foreach (var set in groupedObjects)
{
var task = Task.Run(async () => await bulkheadPolicy.ExecuteAsync(async () => DoSomething(set)));
parallelTasks.Add(task);
};
await Task.WhenAll(parallelTasks);
DoSomething是一种对一组对象执行操作的异步方法。我希望在并行线程中发生这种情况,同时尊重polly的重试策略并允许进行限制。
但是,在处理任务/线程的方式方面,我似乎一直对Parallel.ForEach和使用Bulkhead.ExecuteAsync的确切功能行为感到困惑。
答案 0 :(得分:1)
使用.as-console {background-color:black !important; color:lime;}
.as-console-wrapper {max-height:100% !important; top:0;}
可能会破坏隔板的目的,这可能是正确的。我认为一个简单的延迟循环将为舱壁提供任务。尽管我猜想在现实生活中的示例中将有连续的数据流,而不是预定义的列表或数组。
Parallel.ForEach
输出:
using Polly;
using Polly.Bulkhead;
static async Task Main(string[] args)
{
var groupedObjects = Enumerable.Range(0, 10).Select(n => new object[] { n }); // Create 10 sets to work with
var bulkheadPolicy = Policy.BulkheadAsync(3, 3); // maxParallelization, maxQueuingActions
var parallelTasks = new List<Task>();
foreach (var set in groupedObjects)
{
Console.WriteLine($"Scheduling, Available: {bulkheadPolicy.BulkheadAvailableCount}, QueueAvailable: {bulkheadPolicy.QueueAvailableCount}");
var task = bulkheadPolicy.ExecuteAsync(async () => // Schedule the task and return immediately
{
await DoSomethingAsync(set).ConfigureAwait(false); // Await the task in another thread without capturing the context
});
parallelTasks.Add(task);
await Task.Delay(50); // Interval between scheduling more tasks
}
var whenAllTasks = Task.WhenAll(parallelTasks);
try
{
await whenAllTasks; // Await all the tasks (await throws only one of the exceptions)
}
catch
{
whenAllTasks.Exception.Handle(ex => ex is BulkheadRejectedException); // Ignore rejections, rethrow other exceptions
}
Console.WriteLine($"Processed: {parallelTasks.Where(t => t.Status == TaskStatus.RanToCompletion).Count()}");
Console.WriteLine($"Faulted: {parallelTasks.Where(t => t.IsFaulted).Count()}");
}
static async Task DoSomethingAsync(IEnumerable<object> set)
{
await Task.Delay(500).ConfigureAwait(false); // Pretend we are doing something with the set
}
更新:Scheduling, Available: 3, QueueAvailable: 3
Scheduling, Available: 2, QueueAvailable: 3
Scheduling, Available: 1, QueueAvailable: 3
Scheduling, Available: 0, QueueAvailable: 3
Scheduling, Available: 0, QueueAvailable: 2
Scheduling, Available: 0, QueueAvailable: 1
Scheduling, Available: 0, QueueAvailable: 0
Scheduling, Available: 0, QueueAvailable: 0
Scheduling, Available: 0, QueueAvailable: 0
Scheduling, Available: 0, QueueAvailable: 1
Processed: 7
Faulted: 3
的更实际版本,实际上迫使CPU做一些实际工作(四核计算机中的CPU利用率接近100%)。
DoSomethingAsync
此方法未针对所有数据集运行。它仅针对未被舱壁拒绝的布景运行。