如何以正确的方式实现我的异步方法



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

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

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

如果你想要一个简短(但仍然全面)的教程,我推荐我自己的async/await介绍博客文章,还有其他几个教程。一旦您了解了一些async,下一个好的资源就是官方的async/await常见问题解答。

频道9上也有很多很棒的async视频。他们中的许多人(大多数人)在async还是CTP时讨论它,但在它投入生产时很少进行设计更改。

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

对我调用另一个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修饰符告诉编译器,如果方法中出现任何歧义,则将等待作为关键字而不是标识符来处理。这确保了在C#5.0之前编写的可能使用wait作为标识符的代码在编译时仍然不会出错。async修饰符只能应用于返回void或Task/Task<TResult>的方法和lambda表达式。

在遇到await表达式时,执行(通常)返回给调用者——就像在itterator中使用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中,您将不得不相应地处理它。

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

最新更新