我想知道如何实现我自己的异步方法的最佳方式。我已阅读this article,它说,将同步方法封装到任务中并将其用作异步方法并不是一个好主意,因为这种方式使用其他线程并使用更多资源,这就是我们想要避免使用异步的方法。如何以正确的方式实现我的异步方法?
在文章中使用TaskCompletionSource,但我不知道如何使用它。 TaskCompletionSource的目的是什么?
我想知道如何实现我自己的异步方法的最佳方式。我已阅读this article,它说,将同步方法封装到任务中并将其用作异步方法并不是一个好主意,因为这种方式使用其他线程并使用更多资源,这就是我们想要避免使用异步的方法。如何以正确的方式实现我的异步方法?
在文章中使用TaskCompletionSource,但我不知道如何使用它。 TaskCompletionSource的目的是什么?
编写async
方法的最佳资源是Task-based Asynchronous Pattern (TAP) document by Stephen Toub。该文件中的大部分信息现在是part of the official MSDN documentation。
如果你想要一个较短的(但仍然是彻底的)教程,我推荐自己的async
/await
intro blog post,还有其他几个教程。一旦你了解了一下async
,下一个好资源是official async
/await
FAQ。
Channel9上还有很多很棒的async
视频。当它仍然是CTP时,它们中的很多(大多数?)讨论了async
,但是当它投入生产时很少进行设计更改。
要回答您的具体问题,请使用TaskCompletionSource
为现有异步操作(例如,I/O操作,计时器或事件)创建一个Task
包装。您不能执行由TaskCompletionSource
创建的Task
中的代码;您应该使用Task.Run
在Task
中执行代码。
在另一个async
方法(使用.NET 5.0 async
关键字)内调用方法很好。我不确定我完全理解你的问题,但请看下面的简单例子。要出发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之前编写的可能使用await作为标识符的代码仍然无错地编译。异步修饰符只能应用于返回void或Task
/Task<TResult>
的方法和lambda表达式。
在遇到await
表达式时,执行(通常)返回给调用者 - 而非像在使用yeild return
一样。但是在返回之前,运行时会为等待的任务附加一个延续,以确保当任务完成时,执行会跳回到继续停止的方法中。我想你可以写上面的方法,显示编译器将其转换为什么
void DisplayPrimesCount()
{
var awaiter = GetPrimesCountAsync(2, 100000).GetAwaiter();
awaiter.OnCompleted(() =>
{
int result = awaiter.GetResult();
Console.WriteLine(result);
});
}
我希望这有助于。
编辑。这样可以避免任务阻塞(这是很难说你的情况是什么,你已经张贴任何代码),你需要把你想创建“一个级别”
private async Task<bool> TestAsync()
{
return await Task.Run(() =>
{
// Do stuff non-blocking.
return true;
}).ConfigureAwait(continueOnCapturedContext:false);
}
TestAsync
这里会把async
过程能够通过在线程池线程而不是UI上下文上执行它的“return”语句来完成。这将立即返回给呼叫者,但请注意,但是,如果您继续使用Task.Result
,则任何异常都会被包装在AggregateException
中,您必须相应地处理这些异常。
可以找到一个很好的解释on MSDN here。在进入async
/await
之前,我会推荐学习C#4.0任务,因为它会让您更好地理解并发性。
我的理解正确的文章,问题是,如果我使用Tas.Run,我创建一个新的线程,并且这使用更多的资源,所以目标是不使用异步方法的新线程。问题是,到目前为止,我不知道如何在没有任务的情况下实现异步方法。运行而不是阻止用户界面。本文使用TaskCompletionSource,它不创建一个新线程,但我不知道如何以正确的方式使用它。 –
请参阅编辑一些更多的说明。 – MoonKnight
听起来像是你误解了运行“异步”的概念,因为它基本上总是涉及第二个线程。如果你不习惯创建/删除额外的线程,你可以创建一个额外的线程,等待事情处理(不忙循环!),基本上重用它的资源。缺点是,每次运行只能处理一个异步请求,但它会起作用。 – Mario
但是如果我使用task.Run创建一个新线程,并且如果我没有错,这将创建一个新线程,这是我们想要避免的原因,因为使用更多资源并且可扩展性更差。是否有可能在不创建新线程的情况下在任务中执行代码? –
代码将在哪里运行? –
是的。然后,如果我使用像killercam这样的代码,那就是我是doinf,它与在任务中包装同步方法以创建异步方法的方法相同。如果这是真的,在我的文章中,作者说这不是一个好习惯。 –