【问题标题】:Semaphore thread throttling with async/await使用 async/await 限制信号量线程
【发布时间】:2013-07-11 08:34:00
【问题描述】:

我最近遇到了一个为异步/等待调用限制线程的示例。在我的机器上分析和使用代码之后,我想出了一种稍微不同的方法来做同样的事情。我不确定的是幕后发生的事情是否几乎相同,或者是否有任何值得注意的细微差异?

这是基于原始示例的代码:

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 创建的任务并且是结果有点瘦了。这是准确的还是我在这里偏离了基础?

【问题讨论】:

    标签: c# multithreading async-await


    【解决方案1】:

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

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

    【讨论】:

    • 谢谢。这正是我正在寻找的:从裂缝中溜走的实施副作用。感觉就像 async-await 有很多这样的东西。我将做一些关于如何处理异常的工作。我对 TPL 中的 AggregateException 很熟悉,但没有足够的经验。
    • 实际上更多的是来自 TPL 的剩余物——很少——对 async 有用,但更多时候不仅仅是妨碍。例如,Task 构造函数、StartWaitResultContinueWithWaitAllWaitAny 用于 parallel(不是 asynchronous) 编程,应该避免在async 世界中,除非你真的知道自己在做什么。
    • 我正在查看一些视频,其中有些微妙但很重要。 .GetAwaiter().GetResult() 返回异常而不是包装器。他们建议使用它而不是 .Result。
    • @hmadrigal:是的;如果你必须阻止,GetAwaiter().GetResult()Result 好。但如果可能,最好使用await
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多