簡體   English   中英

限制Async方法的並行性,而不是阻塞線程池線程

[英]Limit parallelism of an Async method and not block a Thread-Pool thread

我有一個異步方法RequestInternalAsync() ,它向外部資源發出請求,並希望編寫一個包裝器方法,通過減少並行性來限制對該方法的多個並發異步請求。

首先考慮的第一個選項是具有有限並發性的TaskSchedulerLimitedConcurrencyLevelTaskSchedulerConcurrentExclusiveSchedulerPair等)。

但是要使用自定義調度程序運行任務,我必須使用僅接受Action<>TaskFactory啟動任務,即我不能通過不阻止額外線程等待內部方法的執行來實現。

第二個選項是SemaphoreSlim ,它完成了它的工作,但是在這種情況下我自己實現了限制,而不是使用TaskScheduler

static void Main(string[] args)
{
    // TESTING 1

    var task1 = Task.WhenAll(Enumerable.Range(1, 10).Select(i => RequestAsyncBad()));

    task1.Wait();

    // TESTING 2

    var task2 = Task.WhenAll(Enumerable.Range(1, 10).Select(i => RequestAsyncBetter()));

    task2.Wait();
}

private static Task RequestInternalAsync()
{
    return Task.Delay(500);
}

解決方案#1:

private static readonly ConcurrentExclusiveSchedulerPair _concurrentPair
    = new ConcurrentExclusiveSchedulerPair(TaskScheduler.Default, 2);

public static Task RequestAsyncBad()
{
    // Dumb: Because TaskFactory doesn't provide an overload which accepts another task, only action.
    // As result, we blocking a thread to just wait until the inner task finishes.

    return Task.Factory.StartNew(() => RequestInternalAsync().Wait(),
        CancellationToken.None, TaskCreationOptions.DenyChildAttach, _concurrentPair.ConcurrentScheduler);
}

解決方案#2(更好):

private static readonly SemaphoreSlim _semaphore = new SemaphoreSlim(2);

public static async Task RequestAsyncBetter()
{
    // Here we don't waste thread-pool thread on waiting for a completion of inner task,
    // but instead of using TaskScheduler, implementing a hand-made stuff with semaphore. 

    await _semaphore.WaitAsync().ConfigureAwait(false);

    try
    {
        await RequestInternalAsync();
    }
    finally
    {
        _semaphore.Release();
    }
}

更優雅的方式是什么?

  • 重用TPL和TaskScheduler標准Task API
  • 而不是阻止額外的線程

TaskScheduler僅對CPU綁定工作有用。 你的工作不是使用線程。 它使用IO完成端口,這意味着您的網絡呼叫根本不包含任何線程。 沒有辦法讓TaskScheduler用於IO操作。

如果您還不確定:.NET中的異步IO基於使用TaskCompletionSource ,它不會以最輕微的方式綁定到線程或調度程序。

SemaphoreSlim是正確的方法。 或者,創建一個ServicePoint並設置其最大並發性。 僅適用於HTTP請求。

請注意,如果您發現自己使用Wait那么您應該猶豫並思考您正在做什么。 通常,這是一個錯誤。

public static async Task RequestAsyncCool()
{
    await Task.Factory.StartNew(async () => {
            await RequestInternalAsync();
        },
        CancellationToken.None, 
        TaskCreationOptions.DenyChildAttach, 
        TaskScheduler.Current);
}

你真的不應該Wait任務。 請參閱https://www.google.com/search?q=task+wait+deadlock

你看過TPL DataFlow了嗎? 這可能只是你的事情......

暫無
暫無

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

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