簡體   English   中英

澄清與限制並行運行多個異步任務

[英]Clarification on running multiple async tasks in parallel with throttling

編輯:由於 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 - 操作: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 的功能行為究竟是什么感到困惑。

您可能是對的,使用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}");

        // Start the task
        var task = bulkheadPolicy.ExecuteAsync(async () =>
        {
            // Await the task without capturing the context
            await DoSomethingAsync(set).ConfigureAwait(false);
        });
        parallelTasks.Add(task);
        await Task.Delay(50); // Interval between scheduling more tasks
    }

    var whenAllTasks = Task.WhenAll(parallelTasks);
    try
    {
        // Await all the tasks (await throws only one of the exceptions)
        await whenAllTasks;
    }
    catch when (whenAllTasks.IsFaulted) // It might also be canceled
    {
        // Ignore rejections, rethrow other exceptions
        whenAllTasks.Exception.Handle(ex => ex is BulkheadRejectedException);
    }
    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)
{
    // Pretend we are doing something with the set
    await Task.Delay(500).ConfigureAwait(false);
}

輸出:

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

在 Fiddle 上試試


更新:稍微更真實的DoSomethingAsync版本,它實際上強制 CPU 做一些實際工作(在我的四核機器中 CPU 利用率接近 100%)。

private static async Task DoSomethingAsync(IEnumerable<object> objects)
{
    await Task.Run(() =>
    {
        long sum = 0; for (int i = 0; i < 500000000; i++) sum += i;
    }).ConfigureAwait(false);
}

此方法並未針對所有數據集運行。 它僅針對不被隔板拒絕的集合運行。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM