首页 > 解决方案 > ContinueWith 方法返回的任务不需要 Wait() 方法?

问题描述

为什么即使我不将Wait()方法用于方法返回的第二个任务,代码也能工作ContinueWith

如果我不对Wait()第一个任务使用该方法,那么什么都行不通,但对于第二个任务则不行。

using System;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            void MyMethod1()
            {
                Console.WriteLine(1);
            }

            void MyMethod2(Task MyTask)
            {
                Console.WriteLine(2);
            }

            Task MyTask1 = new Task(MyMethod1);

            MyTask1.Start();
            MyTask1.Wait();

            Task MyTask2 = MyTask1.ContinueWith(MyMethod2); //Why it is work?
            //MyTask2.Wait();
        }
    }
}

标签: c#task

解决方案


发生这种情况是因为控制台应用程序在后台任务将输出写入控制台之前退出。

如果您添加Console.ReadLine()到方法的末尾并尝试在没有任何内容的情况下运行它,您.Wait()将看到它按预期输出。12

那么,为什么会发生这种情况?

嗯,这是因为第一次写入控制台会产生开销——后续写入不会发生这种开销。

所以当你没有Wait()第一个Console.WriteLine()还没有来得及完成之前,程序就退出了。

但是,当您将第一个任务添加Wait()到第一个任务时,第一个任务Console.WriteLine()(显然)已经完成,而第二个任务Console.WriteLine()没有与第一个任务相关的开销,并且它有时间在程序退出之前完成。

(这是一个竞态条件,实际结果可能会因不同的运行和不同的环境而有所不同。)

Console.WriteLine()我们可以执行一些基本的计时来显示第一次调用和第二次调用之间的时间差异。(注意:通常我使用 Bench.Net 进行计时,但在这种特殊情况下效果不佳。)

static void Main()
{
    Stopwatch sw = Stopwatch.StartNew();
    Console.WriteLine("1");
    sw.Stop();
    Console.WriteLine("First Console.WriteLine() took " + sw.Elapsed);

    sw.Restart();
    Console.WriteLine("2");
    sw.Stop();
    Console.WriteLine("Second Console.WriteLine() took " + sw.Elapsed);
}

对于我的 PC 上的发布版本,此输出:

1
First Console.WriteLine() took 00:00:00.0060951
2
Second Console.WriteLine() took 00:00:00.0000624

如您所见,第二次调用几乎比第一次调用快 100 倍。如果程序在几秒钟内退出0.0060951,则不会看到任何输出。

但是如果 aConsole.WriteLine()之前已经执行过,程序将不得不在不到0.0000624几秒的时间内退出,输出不会出现(大约!)。

您还可以通过Console.WriteLine()在程序开头添加 a 来演示这一点,以便后续调用避免第一次调用的开销:

Console.WriteLine("Priming");

void MyMethod1()
{
    Console.WriteLine(1);
}

void MyMethod2(Task MyTask)
{
    Console.WriteLine(2);
}

Task MyTask1 = new Task(MyMethod1);
MyTask1.Start();
Task MyTask2 = MyTask1.ContinueWith(MyMethod2); //Why it is work?

如果你这样做,输出是1and 2- 即使没有调用MyTask1.Wait();.

再次注意,这是一个竞争条件,实际结果可能会有所不同。它依赖于在WriteLine()程序实际退出之前完成的调用。

您从中得出的结论应该是多线程很棘手,您必须正确执行,否则会发生这样的奇怪事情!


推荐阅读