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

AFM*_*AFM 12 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");
}
Run Code Online (Sandbox Code Playgroud)

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

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");
}
Run Code Online (Sandbox Code Playgroud)

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

Ste*_*ary 13

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

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

  • 事实上,更多的是来自TPL的残羹剩饭 - 很少 - 与`async`一起使用,但通常不仅仅是妨碍.例如,`Task`构造函数,`Start`,`Wait`,`Result`,`ContinueWith`,`WaitAll`和`WaitAny`用于*parallel*(不是*asynchronous*)编程,应避免使用"异步"世界除非你真的知道自己在做什么. (2认同)