信号量线程使用async / await进行限制

时间:2013-07-12 17:50:22

标签: c# multithreading async-await

我最近遇到了一个为async / await调用限制线程的例子。在我的机器上分析和播放代码后,我想出了一个稍微不同的做同样事情的方法。我不确定的是,在引擎盖下发生的事情几乎是相同的,或者是否有任何微妙的差异值得注意?

以下是基于原始示例的代码:

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

这是我对相同代码的看法:

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

我可能会离开,但似乎Task.Run方法正在创建一个运行LongRunningTask()的任务,然后添加一个继续来打印结果,而我的方法绕过Task.Run创建的任务,并且结果有点精简。这是准确的,还是我离开基地?

1 个答案:

答案 0 :(得分:13)

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

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