.Net Task类 - 请解释一下

时间:2015-06-19 06:43:16

标签: c# .net asynchronous async-await task

有人可以解释一下为什么下面的公共异步任务DoStuff()方法仍可以在不返回任何内容的情况下工作吗?它没有说空,所以我认为返回类型必须是任务

当我从 DoStuff()方法中删除 async 等待关键字时,编译器会给我一个“并非全部代码路径返回值“错误。但是,如果我添加了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;
    }
}

}

6 个答案:

答案 0 :(得分:2)

任务基本上是promise (or future)。它“承诺”您启动的异步方法最终将完成,完成任务。使用任务对象,以便您可以获取有关任务何时完成的信息。还有generic version任务,它还承诺在任务完成时获得值。

启动异步方法后,您将获得Task个对象。该方法几乎立即返回,但实际工作可能稍后完成。然后,您可以wait完成任务以阻止当前线程,并等待异步方法完成。

当您自己进行异步执行时 - 这通常是您在调用异步方法时要执行的操作 - 然后您可以使用任务对象上的await关键字等待这些任务。这实际上暂停了您当前所在的异步方法,并在您等待的任务完成后立即返回执行。

await关键字仅在异步方法中可用,由async方法说明符指示。这些方法将自动返回返回值的任务对象:如果没有从异步方法中明确地返回任何内容,则返回Task对象;如果从异步方法返回类型为T的对象,它实际上会返回一个您可以等待的Task<T>对象。然后,任务完成后,Task<T>类型可以“解压缩”。这允许您获取T类型的实际对象。

最后,async方法也可以不返回任何内容,void,以使它们“发射并忘记”。如果你调用一个返回类型为void的异步方法,它将被异步执行(“fire”),但你没有任何方法知道它何时完成,因为你没有任务对象等待(“忘记”)。这就是为什么你想要一般避免使用async void方法(它们对于异常处理也是坏的)并且总是使用“真正的”等待的异步方法(那些返回一些任务对象的方法)。但您仍然可以使用async void方法启动异步执行而不会阻止主线程。否则,您可以通过调用任务上的Wait()方法来阻止它。

有关详细信息,请查看以下链接:

答案 1 :(得分:1)

  

为什么下面的公共异步任务DoStuff()方法仍可以在不返回任何内容的情况下工作?

因为编译器允许它。使用Task修饰符标记方法时,会创建一个实际为您返回Task的状态机。你可以在任何解编译器中看到它。

  

当我从DoStuff()方法中删除async和await关键字时,编译器给出了“并非所有代码路径返回值”错误。

因为不再创建一个返回Task的状态机,所以你现在必须自己动手,因为没有更多的编译器魔法。

  

究竟什么是任务?

正如其他人所说,只是承诺将来会完成的工作。 async-await可以表示很多东西,其中一个是异步操作。魔术伴随着Task个关键词。编译器魔术与GetAwaiter具有特殊关系,但可以等待实现{{1}}方法的任何类型。有关here

的更多信息

答案 2 :(得分:0)

这是因为async/await是神奇的。好吧,它不是真正的魔术,但是async/await编译器正在重写你的方法,以便它返回Task。就像CountToFifty()返回Task<string>一样,但您的方法会返回string

Task本身并不特别。它只是一个普通的.Net类,用于表示可能尚未完成的操作。

如果没有async / await,编译器就不会修改DoStuff(),因此它会让你返回一个Task对象。

答案 3 :(得分:0)

Async方法不会立即返回。方法可能需要查询外部源。这需要时间 - 其他代码可以运行。这是Method中await-async的目的。因此,当我们使用await关键字时,并不总是需要在Async方法中返回。

查看Explanation of Tasks by DotNetPearls了解更多信息。

答案 4 :(得分:0)

正如其他人所说,任务是一个承诺&#34;或者&#34;未来&#34; - 也就是说,它代表了将来可能完成的一些操作。

Task表示没有返回值的操作,Task<T>表示返回值为T的操作。

请注意Task非常有用(而不是void),因为操作可能成功完成或异常(或取消),Task能够代表这些结束的状态。

  

有人可以解释一下为什么下面的公共异步任务DoStuff()方法仍可以在不返回任何内容的情况下工作吗?

async关键字将为您的方法构造一个状态机,状态机将创建一个表示该方法的Task对象。如果您的async方法返回一个值,则状态机会将该返回值放在Task<T>上。如果您的async方法抛出异常,状态机会将该异常放在Task / Task<T>上。

  

究竟什么是任务?

我已经在Task / async的背景下描述了await的内容。部分混淆是因为Task在以其他方式使用时具有非常不同的含义。我将Promise Task用于异步任务,而Delegate Task用于代码运行任务。

async状态机创建的任务始终是Promise Tasks。一般来说,异步任务应该是await,而不是Wait。你应该避免使用async void(因为状态机无法表示方法,因此它具有令人惊讶的异常处理语义)。

您可能会发现我的async intro和我的article on best practices一样有用。

答案 5 :(得分:0)

另一种看待这种情况的方法是,假设您有4件事要做,以回应客户的http请求。

  1. 在数据库中输入订单。 Task.Run(PlaceOrder)
  2. 向客户发送一封感谢邮件。 Task.Run(SendEmail)
  3. 向您的履行中心api发送请求。 Task.Run(PlaceOrder)
  4. 记录会话请求。 Task.Run(LogRequest)
  5. 因此,不是谈论合同,承诺,状态服务器等。任务是线程,可以跨所有核心/处理器传播工作。我有4个核心和8个处理器。如果我执行上述任务,我可以看到所有8个处理器都在运行,速度超过两倍。那是一个任务。

    它变得更好。现在所有8个处理器上都在运行,但我们可以让它更快。虽然它们在所有8个处理器上运行,但它们轮流或排队等待彼此完成。让我们说平均每个任务需要5秒钟,所以我对客户的响应时间是20秒,但我们只是减少了一半。如果我使用async / await,我可以同时推送所有4个任务,所以它们都不会互相等待。所以现在我可以看到所有8个处理器,但现在他们使用了更多的CPU和wow64他们完成了。

    但等待它们仍然很慢,因为发送订单的api请求需要8秒钟,客户和其他任务必须等待它完成。这是它变得非常好的地方。不要等回复客户并感谢他们的订单。所以现在你只需要在3毫秒而不是20秒内将交易转回给客户。围绕4个任务包装try / catch并记录任何失败,以便有人可以使用它们。希望这已经完成而不是告诉客户哦,抱歉我们的邮件服务器已关闭。我听说有些人称之为忘记和别的东西,这非常糟糕。不算太差。这是很好的编程。坚固的主流线程/异步编程是过时的。购买永远不会被使用的内核和内存以及用户处理缓慢响应等待他们不关心的事情将有望成为过去。

    那是task / async / await。

    var myTask = Task.Run(()=&gt; {doSomething});.