1. ホーム
  2. c#

[解決済み] 非同期I/Oの同時処理量を制限するには?

2022-06-27 08:04:37

質問

// let's say there is a list of 1000+ URLs
string[] urls = { "http://google.com", "http://yahoo.com", ... };

// now let's send HTTP requests to each of these URLs in parallel
urls.AsParallel().ForAll(async (url) => {
    var client = new HttpClient();
    var html = await client.GetStringAsync(url);
});

ここで問題なのは、1000以上の同時ウェブリクエストを開始することです。これらの非同期 http リクエストの同時実行量を制限する簡単な方法はありますか? 常に 20 以上の Web ページがダウンロードされないようにするためです。最も効率的な方法でそれを行うにはどうすればよいですか?

どのように解決するのですか?

.NET 4.5 Beta を使用して、.NET 用の async の最新バージョンで間違いなくこれを行うことができます。usr」からの前の投稿は、Stephen Toub によって書かれた良い記事を指していますが、あまり発表されていないニュースは、async セマフォが実際に .NET 4.5 のベータ リリースに入ったというものです。

私たちの愛する SemaphoreSlim クラスを見てみましょう (これはオリジナルの Semaphore を使用する必要があります)、現在では WaitAsync(...) シリーズのオーバーロードを誇ります。期待されるすべての引数 - タイムアウト間隔、キャンセルトークン、いつものスケジューリングの仲間たち - があります。)

Stephen はまた、ベータ版で登場した新しい .NET 4.5 の機能についての最近のブログ記事も書いています。 .NET 4.5 Beta の並列処理に関する新機能 .

最後に、SemaphoreSlimを使った非同期メソッドのスロットリングについてのサンプルコードを紹介します。

public async Task MyOuterMethod()
{
    // let's say there is a list of 1000+ URLs
    var urls = { "http://google.com", "http://yahoo.com", ... };

    // now let's send HTTP requests to each of these URLs in parallel
    var allTasks = new List<Task>();
    var throttler = new SemaphoreSlim(initialCount: 20);
    foreach (var url in urls)
    {
        // do an async wait until we can schedule again
        await throttler.WaitAsync();

        // using Task.Run(...) to run the lambda in its own parallel
        // flow on the threadpool
        allTasks.Add(
            Task.Run(async () =>
            {
                try
                {
                    var client = new HttpClient();
                    var html = await client.GetStringAsync(url);
                }
                finally
                {
                    throttler.Release();
                }
            }));
    }

    // won't get here until all urls have been put into tasks
    await Task.WhenAll(allTasks);

    // won't get here until all tasks have completed in some way
    // (either success or exception)
}

最後になりますが、おそらく言及に値するのは、TPL ベースのスケジューリングを使用するソリューションです。まだ開始されていないデリゲートバインドタスクをTPL上に作成し、カスタムタスクスケジューラで同時実行数を制限できるようにします。実際、そのための MSDN サンプルがここにあります。

また タスクスケジューラ .