How to limit the amount of concurrent async I/O operations?

前端 未结 14 2466
遇见更好的自我
遇见更好的自我 2020-11-22 01:27
// 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          


        
14条回答
  •  执念已碎
    2020-11-22 02:08

    You can definitely do this in the latest versions of async for .NET, using .NET 4.5 Beta. The previous post from 'usr' points to a good article written by Stephen Toub, but the less announced news is that the async semaphore actually made it into the Beta release of .NET 4.5

    If you look at our beloved SemaphoreSlim class (which you should be using since it's more performant than the original Semaphore), it now boasts the WaitAsync(...) series of overloads, with all of the expected arguments - timeout intervals, cancellation tokens, all of your usual scheduling friends :)

    Stephen's also written a more recent blog post about the new .NET 4.5 goodies that came out with beta see What’s New for Parallelism in .NET 4.5 Beta.

    Last, here's some sample code about how to use SemaphoreSlim for async method throttling:

    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();
        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)
    }
    

    Last, but probably a worthy mention is a solution that uses TPL-based scheduling. You can create delegate-bound tasks on the TPL that have not yet been started, and allow for a custom task scheduler to limit the concurrency. In fact, there's an MSDN sample for it here:

    See also TaskScheduler .

提交回复
热议问题