13

I recently came across an example of throttling threads for async/await calls. After analyzing and playing with the code on my machine, I came up with a slightly different way of doing the same thing. What I'm uncertain about is wether what is happening under the hood is pretty much the same or if there are any subtle differences worth noting?

Here's the code based on the original example:

private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(5);

public async Task CallThrottledTasks()
{
    var tasks = new List<Task>();

    for (int count = 1; count <= 20; count++)
    {
        await _semaphore.WaitAsync();

        tasks.Add(Task.Run(async () =>
            {
                try
                {
                    int result = await LongRunningTask();
                    Debug.Print(result.ToString());
                }
                finally
                {
                    _semaphore.Release();
                }
            }));
    }

    await Task.WhenAll(tasks);

    Debug.Print("Finished CallThrottledTasks");
}

And here's my take on the same code:

private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(5);

public async Task CallThrottledTasks()
{
    var tasks = new List<Task>();

    for (int count = 1; count <= 20; count++)
    {
        await _semaphore.WaitAsync();

        tasks.Add(LongRunningTask().ContinueWith(t =>
        {
            try
            {
                int result = t.Result;
                Debug.Print(result.ToString());
            }
            finally
            {
                _semaphore.Release();
            }
        }));
    }

    await Task.WhenAll(tasks);

    Debug.Print("Finished CallThrottledTasks");
}

I'm probably way off, but It seems like the Task.Run approach is creating a task to run LongRunningTask() and then adds a continuation to print the result whereas my approach bypasses the task created by Task.Run and is a bit leaner as a result. Is this accurate or am I way off base here?

4

1 回答 1

14

它并没有瘦多少,只是一点点。通常,我避免ContinueWithasync代码中使用,因为await它更简洁并且具有更async友好的默认语义。首先针对开发人员时间进行优化,然后针对其他考虑因素进行优化。

您的代码确实稍微改变了语义:在原始代码中,LongRunningTask是从线程池上下文执行的,而在您的代码中,它是从任何CallThrottledTasks上下文执行的。此外,您的代码不会LongRunningTask干净地传播异常;Task<T>.Result会将异常包装在 中AggregateException,而await不会进行任何包装。

于 2013-07-12T18:02:10.393 回答