.Net 任务类 - 请解释
Posted
技术标签:
【中文标题】.Net 任务类 - 请解释【英文标题】:.Net Task class - please explain 【发布时间】:2015-09-05 01:12:27 【问题描述】:谁能解释一下为什么下面的 public async Task DoStuff() 方法仍然可以工作而不返回任何东西?它没有说 void,所以我假设返回类型必须是 Task。
当我从 DoStuff() 方法中删除 async 和 await 关键字时,编译器会给我一个 "not all代码路径返回值” 错误。但是,如果我添加 async 和 await 关键字,它似乎不需要返回类型,尽管方法签名中缺少 void 关键字。没看懂!
究竟什么是任务?微软解释得很糟糕。谢谢。
namespace Async_and_Await_Example
class Program
static void Main(string[] args)
AsyncAwaitDemo demo = new AsyncAwaitDemo();
demo.DoStuff();
for (int i = 0; i < 100; i++)
Console.WriteLine("Working on the Main Thread...................");
public class AsyncAwaitDemo
public async Task DoStuff()
await Task.Run(() =>
CountToFifty();
);
private static async Task<string> CountToFifty()
int counter;
for (counter = 0; counter < 51; counter++)
Console.WriteLine("BG thread: " + counter);
return "Counter = " + counter;
【问题讨论】:
【参考方案1】:为什么下面的 public async Task DoStuff() 方法仍然可以工作而不返回任何内容?
因为编译器允许这样做。当您使用 async
修饰符标记方法时,会创建一个状态机,它实际上会为您返回一个 Task
。你可以在任何反编译器中看到它。
当我从 DoStuff() 方法中删除 async 和 await 关键字时,编译器会给我一个“并非所有代码路径都返回值”错误。
因为不再创建返回Task
的状态机,您现在必须自己做,因为没有更多的编译器魔法。
什么是任务?
正如其他人所说,只是对未来将完成的工作的承诺。 Task
可以代表很多东西,其中之一是异步操作。神奇之处在于旁边的async-await
关键字。编译器魔法与Task
有特殊关系,但任何实现GetAwaiter
方法的类型都可以等待。更多关于here
【讨论】:
【参考方案2】:任务本质上是promise (or future)。它“承诺”你开始的异步方法最终会完成,完成任务。使用任务对象,以便您可以获取有关何时完成任务的信息。还有一个任务的generic version,它也简单地承诺在任务完成时你会得到一个值。
一旦你启动了一个异步方法,你就会得到一个Task
对象。该方法几乎立即返回,但实际工作可能稍后完成。然后您可以wait 让任务完成以阻塞当前线程并等待异步方法完成。
当您自己进行异步执行时(这通常是您在调用异步方法时想要执行的操作),那么您可以在任务对象上使用 await
关键字等待这些任务。这实质上会暂停您当前所在的异步方法,并在您等待的任务完成后立即返回执行。
await
关键字仅在异步方法中可用,由async
方法说明符表示。这些方法会自动返回一个任务对象作为返回值:如果你没有从异步方法显式返回任何东西,它会返回一个Task
对象;如果你从异步方法返回一个T
类型的对象,它实际上会返回一个你可以等待的Task<T>
对象。一旦任务完成,Task<T>
类型就可以被“解包”。这使您可以获取T
类型的实际对象。
最后,async
方法也可以不返回任何内容,void
,使它们“一劳永逸”。如果您调用返回类型为void
的异步方法,它将异步执行(“触发”),但您无法知道它何时完成,因为您没有要等待的任务对象(“忘记”)。这就是为什么您通常要避免使用async void
方法(它们对异常处理也不利)并始终使用“真正的”可等待异步方法(那些返回某些任务对象的方法)。但是您仍然可以使用async void
方法来启动异步执行而不会阻塞您的主线程。否则,您可以通过在任务上调用Wait()
方法来阻止它。
欲了解更多信息,请查看以下链接:
Asynchronous Programming with Async and Await Task-based Asynchronous Pattern Async/Await FAQ Async and Await【讨论】:
【参考方案3】:这是因为async/await
是魔法。好吧,这并不是真正的魔法,但是使用async/await
,编译器正在重写您的方法,以便它返回Task
。就像CountToFifty()
返回一个Task<string>
但你的方法返回一个string
。
Task
本身并没有什么特别之处。它只是一个普通的 .Net 类,用于表示可能尚未完成的操作。
如果没有 async/await,编译器不会修改 DoStuff()
,因此它自己可以返回一个 Task 对象。
【讨论】:
【参考方案4】:由于异步方法不会立即返回。方法可能需要查询外部源。这需要时间——其他代码也可以运行。这就是 Method 中 await-async 的目的。所以当我们使用 await 关键字时,并不总是需要在 Async Method 中返回。
查看Explanation of Tasks by DotNetPearls了解更多信息。
【讨论】:
【参考方案5】:正如其他人所指出的,任务是“承诺”或“未来”——也就是说,它代表了将来可能完成的某些操作。
Task
代表没有返回值的操作,Task<T>
代表返回值为T
的操作。
请注意,Task
(而不是 void
)很有用,因为操作可能成功完成,也可能出现异常(或取消),而 Task
能够表示这些结束状态。
谁能解释一下为什么下面的公共异步任务 DoStuff() 方法仍然可以在不返回任何内容的情况下工作?
async
关键字将为您的方法构造一个状态机,该状态机将创建一个代表该方法的Task
对象。如果您的async
方法返回一个值,则状态机将该返回值放在Task<T>
上。如果您的async
方法抛出异常,状态机将把该异常置于Task
/Task<T>
。
什么是任务?
我已经在async
/await
的上下文中描述了Task
是什么。部分混淆是因为Task
在以其他方式使用时具有非常不同的含义。我将术语 Promise Task 用于异步任务,将 Delegate Task 用于代码运行任务。
async
状态机创建的任务始终是 Promise 任务。一般来说,异步任务应该是await
'ed,而不是Wait
'ed。并且您应该避免使用async void
(因为状态机无法表示该方法,它具有令人惊讶的异常处理语义)。
您可能会发现我的async
intro 以及我的article on best practices 很有帮助。
【讨论】:
【参考方案6】:另一种看法是,假设您有 4 件事要做以响应客户的 http 请求。
-
在数据库中输入订单。 Task.Run(PlaceOrder)
向客户发送一封感谢邮件。 Task.Run(SendEmail)
向您的履行中心 API 发送请求。 Task.Run(PlaceOrder)
记录会话请求。 Task.Run(LogRequest)
因此,与其谈论合同、承诺、状态服务器等,Task 是一种将工作分散到所有内核/处理器上的线程。我有 4 个内核和 8 个处理器。如果我执行上述任务,我可以看到所有 8 个处理器都在运行,而且速度是原来的两倍多。这是一个任务。
它变得更好。这项工作现在在所有 8 个处理器上运行,但我们可以让它更快。尽管它们在所有 8 个处理器上运行,但它们轮流或排队等待对方完成。假设平均每个任务需要 5 秒,所以我对客户的响应时间是 20 秒,但我们只是将其缩短了一半。如果我使用 async/await,我可以同时推送所有 4 个任务,因此它们都不会相互等待。所以现在我可以看到所有 8 个处理器都在运行,但现在它们使用了更多的 cpu 和 wow64,它们已经完成了。
但是等待他们仍然很慢,因为发送订单的 api 请求需要 8 秒,客户和其他任务必须等待它完成。这是它变得非常好的地方。不要等待。回复客户并感谢他们的订单。因此,现在您只需在 3 毫秒而不是 20 秒内将交易转回给客户。围绕 4 个任务进行尝试/捕获并记录任何失败,以便有人可以处理它们。希望这已经完成,而不是告诉客户哦,抱歉我们的邮件服务器已关闭。我听说有些人称它为“忘记”之类的,这非常糟糕。不算太差。是很好的编程。扎实的主流线程/异步编程已经姗姗来迟。购买从未使用过的内核和内存,以及处理缓慢响应等待坦率地说他们不关心的事情的用户有望成为过去。
那是任务/异步/等待。
var myTask = Task.Run(() => doSomething );。
【讨论】:
以上是关于.Net 任务类 - 请解释的主要内容,如果未能解决你的问题,请参考以下文章