Bob*_*man 9 .net c# asynchronous async-await
假设我有一个Windows服务,它正在做一些工作,然后在很长一段时间内一直睡眠,一直到来(直到服务关闭).所以在服务的OnStart中,我可以启动一个线程,其入口点如下:
private void WorkerThreadFunc()
{
while (!shuttingDown)
{
DoSomething();
Thread.Sleep(10);
}
}
Run Code Online (Sandbox Code Playgroud)
在服务的OnStop中,我以某种方式设置了shuttingDown标志然后加入线程.实际上可能有几个这样的线程,以及其他线程,都在OnStart中启动并在OnStop中关闭/加入.
如果我想在基于async/await的Windows服务中执行此类操作,似乎我可以让OnStart创建可取消的任务但不等待(或等待)它们,并让OnStop取消这些任务然后Task.WhenAll ().等等().如果我理解正确,上面显示的"WorkerThreadFunc"的等价物可能是这样的:
private async Task WorkAsync(CancellationToken cancel)
{
while (true)
{
cancel.ThrowIfCancellationRequested();
DoSomething();
await Task.Delay(10, cancel).ConfigureAwait(false);
}
}
Run Code Online (Sandbox Code Playgroud)
问题1:呃......对吗?我是异步/等待的新手,仍然试图了解它.
假设这是正确的,现在让我们说DoSomething()调用是(或包括)某些硬件的同步写入I/O. 如果我理解正确:
问题2:那不好?我不应该在基于async/await的程序中的Task中进行同步I/O操作?因为它在I/O发生时从线程池中占用一个线程,而线程池中的线程是一个非常有限的资源?请注意,我可能会有数十名此类工作人员同时使用不同的硬件.
我不确定我是否理解正确 - 我认为这与Stephen Cleary的文章" Task.Run Etiquette例子:不要使用Task.Run错误的东西 "有关,但这是特别关于它的存在在Task.Run中做阻塞工作很糟糕.我不确定如果我只是直接这样做也不好,就像上面的"私人异步任务工作()"例子一样?
假设这也很糟糕,那么如果我理解正确,我应该使用DoSomething的非阻塞版本(如果它尚不存在则创建非阻塞版本),然后:
private async Task WorkAsync(CancellationToken cancel)
{
while (true)
{
cancel.ThrowIfCancellationRequested();
await DoSomethingAsync(cancel).ConfigureAwait(false);
await Task.Delay(10, cancel).ConfigureAwait(false);
}
}
Run Code Online (Sandbox Code Playgroud)
问题3:但是......如果DoSomething来自第三方库,我必须使用并且不能更改,并且该库不会公开DoSomething的非阻塞版本,该怎么办?它只是一个黑色的盒子,在某些时候阻止写入一块硬件.
也许我包装它并使用TaskCompletionSource?就像是:
private async Task WorkAsync(CancellationToken cancel)
{
while (true)
{
cancel.ThrowIfCancellationRequested();
await WrappedDoSomething().ConfigureAwait(false);
await Task.Delay(10, cancel).ConfigureAwait(false);
}
}
private Task WrappedDoSomething()
{
var tcs = new TaskCompletionSource<object>();
DoSomething();
tcs.SetResult(null);
return tcs.Task;
}
Run Code Online (Sandbox Code Playgroud)
但这似乎只是将问题进一步推进而不是解决问题.WorkAsync()在调用WrappedDoSomething()时仍然会阻塞,并且在WrappedDoSomething()已经完成阻塞工作后才会到达"await".对?
鉴于(如果我理解正确)在一般情况下async/await应该被允许在程序中一直"扩散",这是否意味着如果我需要使用这样的库,我基本上不应该程序async/await-based?我应该回到Thread/WorkerThreadFunc/Thread.Sleep世界?
如果基于异步/等待的程序已经存在,做其他事情,但是现在需要添加使用这种库的其他功能呢?这是否意味着应该将基于异步/等待的程序重写为基于线程/等等的程序?
Ste*_*ary 11
实际上可能有几个这样的线程,以及其他线程,都在OnStart中启动并在OnStop中关闭/加入.
另外,一个"主"线程通常更简单,它将启动/加入所有其他线程.然后OnStart/ OnStop只处理主线程.
如果我想在基于async/await的Windows服务中执行此类操作,似乎我可以让OnStart创建可取消的任务但不等待(或等待)它们,并让OnStop取消这些任务然后Task.WhenAll ().等等().
这是一种完全可以接受的方法.
如果我理解正确,上面显示的"WorkerThreadFunc"的等价物可能是这样的:
可能想要传递CancellationToken下来; 取消也可以由同步代码使用:
private async Task WorkAsync(CancellationToken cancel)
{
while (true)
{
DoSomething(cancel);
await Task.Delay(10, cancel).ConfigureAwait(false);
}
}
Run Code Online (Sandbox Code Playgroud)
问题1:呃......对吗?我是异步/等待的新手,仍然试图了解它.
这没有错,但它只会在Win32服务上为您节省一个线程,这对您没有太大作用.
问题2:那不好?我不应该在基于async/await的程序中的Task中进行同步I/O操作?因为它在I/O发生时从线程池中占用一个线程,而线程池中的线程是一个非常有限的资源?请注意,我可能会有数十名此类工作人员同时使用不同的硬件.
几十个线程并不是很多.通常,异步I/O更好,因为它根本不使用任何线程,但在这种情况下,您在桌面上,因此线程不是非常有限的资源.async最有利于UI应用程序(UI线程特殊且需要释放),以及需要扩展的ASP.NET应用程序(线程池限制可伸缩性).
底线:从异步方法调用阻塞方法并不错,但它也不是最好的.如果有异步方法,请调用它.但是如果没有,那么只需保留阻塞调用并将其记录在该方法的XML注释中(因为异步方法阻塞是相当令人惊讶的行为).
我认为斯蒂芬克莱里的文章"任务.运行礼仪示例:不要使用Task.Run来处理错误的事情"这样的内容很糟糕,但是具体来说,在Task.Run中阻止工作是不好的.
是的,这是专门Task.Run用于包装同步方法并假装它们是异步的.这是一个常见的错误; 它只是将一个线程池线程换成另一个线程池线程.
假设这也很糟糕,那么如果我理解正确,我应该使用DoSomething的非阻塞版本(如果它尚不存在则创建非阻塞版本)
异步更好(在资源利用率方面 - 即使用的线程更少),因此如果您希望/需要减少线程数,则应使用async.
问题3:但是......如果DoSomething来自第三方库,我必须使用并且不能更改,并且该库不会公开DoSomething的非阻塞版本,该怎么办?它只是一个黑色的盒子,在某些时候阻止写入一块硬件.
然后直接调用它.
也许我包装它并使用TaskCompletionSource?
不,这没有任何用处.它只是同步调用它然后返回一个已经完成的任务.
但这似乎只是将问题进一步推进而不是解决问题.WorkAsync()在调用WrappedDoSomething()时仍然会阻塞,并且在WrappedDoSomething()已经完成阻塞工作后才会到达"await".对?
对.
鉴于(如果我理解正确)在一般情况下async/await应该被允许在程序中一直"扩散",这是否意味着如果我需要使用这样的库,我基本上不应该程序async/await-based?我应该回到Thread/WorkerThreadFunc/Thread.Sleep世界?
假设您已经拥有阻止的Win32服务,那么保持它原样可能很好.如果你正在写一个新的,我个人将使它async减少线程和允许异步API,但是,你不要有做任何一种方式.我更喜欢Tasks over Threads,因为从Tasks(包括例外)获得结果要容易得多.
" async一直"规则只有一种方式.也就是说,一旦你调用一个async方法,那么它的调用者应该是async和它的调用者应该async等它并不意味着每一个方法叫用async方法必须是async.
因此,如果您需要使用仅支持异步的API,那么拥有异步Win32服务的一个很好的理由就是如此.这会导致你的DoSomething方法成为async DoSomethingAsync.
如果基于异步/等待的程序已经存在,做其他事情,但是现在需要添加使用这种库的其他功能呢?这是否意味着应该将基于异步/等待的程序重写为基于线程/等等的程序?
不可以.你总是可以阻止一种async方法.有了适当的文档,所以当你从现在开始重用/维护这段代码时,你不要发誓过去的自我.:)
| 归档时间: |
|
| 查看次数: |
3093 次 |
| 最近记录: |