3

我想知道如何实现我自己的异步方法的最佳方式。我读过这篇文章,它说将同步方法包装到任务中并将其用作异步方法不是一个好主意,因为这种方式使用其他线程并使用更多资源,这就是我们想要避免使用异步.

在文章中使用TaskCompletionSource,但我不知道具体如何使用。TaskCompletionSource 的目的是什么?

4

2 回答 2

4

编写async方法的最佳资源是Stephen Toub 的基于任务的异步模式 (TAP) 文档。该文档中的大部分信息现在已成为 MSDN 官方文档的一部分

如果你想要一个更短(但仍然很全面)的教程,我推荐我自己的async/await介绍性博客文章,还有其他几个教程。一旦你了解了一点async,下一个很好的资源就是官方async/ awaitFAQ

asyncChannel9 上还有大量精彩视频。他们中的许多人(大多数?)都在讨论async它何时还是 CTP,但在投入生产时很少进行设计更改。

为了回答您的具体问题,TaskCompletionSource用于Task围绕现有异步操作(例如,I/O 操作、计时器或事件)创建包装器。您不能执行Task创建者中的代码TaskCompletionSource;您应该Task.Run使用Task.

于 2012-12-11T14:57:19.817 回答
2

在另一个方法中调用一个async方法(使用 .NET 5.0async关键字)对我来说很好。我不确定我是否完全理解你的问题,但举一个简单的例子。为了显示开始int和计数之间的质数,我们可以写

async void DisplayPrimes()
{
    int result = await GetPrimesCountAsync(2, 10000);
    Console.WriteLine(result);
}

在哪里

Task<int> GetPrimesCountAsync(int start, int count)
{
    return Task.Run(() => 
        ParrallelEnumerable.Range(start, count).COunt(n => 
            Enumerable.Range(2, (int)Math.Sqrt(n) - 1).All(i => n % i > 0)));
}

此处async修饰符告诉编译器如果在该方法中出现任何歧义,则将 await 视为关键字而不是标识符。这确保了在 C# 5.0 之前编写的可能使用 await 作为标识符的代码仍然可以无错误地编译。async 修饰符只能应用于返回 void 或Task/的方法和 lambda 表达式Task<TResult>

在遇到await表达式时,执行(通常)返回给调用者 - 就像yeild return在迭代器中一样。但是在返回之前,运行时会为等待的任务附加一个延续,确保当任务完成时,执行会跳回到方法并从中断的地方继续。我想您可以编写上面的方法来显示编译器将其转换为的内容

void DisplayPrimesCount()
{
    var awaiter = GetPrimesCountAsync(2, 100000).GetAwaiter();
    awaiter.OnCompleted(() =>
    {
        int result = awaiter.GetResult();
        Console.WriteLine(result);
    });
}

我希望这有帮助。


编辑。所以为了避免任务阻塞(很难说出你的情况是什么,因为你没有发布任何代码),你需要把async你想要创建的过程“提升一个层次”

private async Task<bool> TestAsync()
{
    return await Task.Run(() => 
    {
        // Do stuff non-blocking.            
        return true;
    }).ConfigureAwait(continueOnCapturedContext:false);
}

TestAsync这里将能够通过在线程池线程而不是 UI 上下文上执行其“return”语句来完成。这将立即返回给调用者,但请注意,如果您继续使用Task.Result,那么任何异常都会被包裹在AggregateException您必须相应处理的 an 中。

可以在 MSDN here上找到一个很好的解释。我建议在进入async/之前学习 C# 4.0 任务,await因为它可以让你更好地理解并发性。

于 2012-12-11T11:06:45.717 回答