阐明了与节流并行运行多个异步任务

时间:2019-04-10 22:37:19

标签: c# asynchronous parallel-processing parallel.foreach polly

编辑:由于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的确切功能行为感到困惑。

1 个答案:

答案 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

此方法未针对所有数据集运行。它仅针对未被舱壁拒绝的布景运行。

相关问题