为什么Task甚至在等待时完成

时间:2015-09-04 05:18:18

标签: c# async-await task-parallel-library task

我在以下代码中遇到问题:

static void Main (string[] args)
{
    Task newTask = Task.Factory.StartNew(MainTask);
    newTask.ContinueWith ((Task someTask) => 
    {
        Console.WriteLine ("Main State=" + someTask.Status.ToString () + " IsFaulted=" + someTask.IsFaulted+" isComplete="+someTask.IsCompleted);
    });
    while (true) 
    {

    }
}

static async Task MainTask()
{
    Console.WriteLine ("MainStarted!");
    Task someTask = Task.Factory.StartNew (() => 
    {
        Console.WriteLine ("SleepStarted!");
        Thread.Sleep(1000);
        Console.WriteLine ("SleepEnded!");
    });
    await someTask;
    Console.WriteLine ("Waiting Ended!!");
    throw new Exception ("CustomException!");
    Console.WriteLine ("NeverReaches here!!");
}

我只想从新启动的任务MainTask中获取异常。但结果并不是我的预期。

MainStarted!
Main State = RanToCompletion IsFaulted = False isComplete = True
SleepStarted!
SleepEnded!
Waiting Ended!!

正如您可以看到结果,任务在等待结束之前完成!!"控制台日志。 即使在MainTask内有MainTask命令,我也不知道为什么await结束了? 我错过了什么吗?

3 个答案:

答案 0 :(得分:18)

Task.Factory.StartNew does not understand async delegates所以在这种情况下您需要使用Task.Run,并且异常应该流过。

Task.Factory.StartNew(MainTask);

基本上等同于

Task.Factory.StartNew(() => MainTask);

忽略来自MainTask的返回任务,异常被吞下。

有关详细信息,请参阅此blog post

尝试使用Task.Run代替你,你将获得例外:

void Main(string[] args)
{
    Task newTask = Task.Run(MainTask);
    newTask.ContinueWith((Task someTask) =>
   {
       Console.WriteLine("Main State=" + someTask.Status.ToString() + " IsFaulted=" + someTask.IsFaulted + " isComplete=" + someTask.IsCompleted);
   });
    while (true)
    {

    }
}

static async Task MainTask()
{
    Console.WriteLine("MainStarted!");
    Task someTask = Task.Run(() =>
   {
       Console.WriteLine("SleepStarted!");
       Thread.Sleep(1000);
       Console.WriteLine("SleepEnded!");
   });
    await someTask;
    Console.WriteLine("Waiting Ended!!");
    throw new Exception("CustomException!");
    Console.WriteLine("NeverReaches here!!");
}

答案 1 :(得分:6)

这里有很好的答案,但我想指出显而易见的事实 - Task.Factory.StartNew完全是多余的,不必要的,使用错误。

如果您更换

Task newTask = Task.Factory.StartNew(MainTask);

Task newTask = MainTask();

您将获得您期望的行为,而不会浪费另一个线程池线程来启动另一个线程池线程。事实上,如果你想重写你的例子更加惯用,你可以使用这样的东西:

static void Main (string[] args)
{
    var task = 
      MainTask()
      .ContinueWith(t => Console.WriteLine("Main State={0}", t.Status));

    task.Wait();
}

static async Task MainTask()
{
    Console.WriteLine ("MainStarted!");

    await Task.Delay(1000);

    Console.WriteLine ("Waiting Ended!!");

    throw new Exception ("CustomException!");

    Console.WriteLine ("NeverReaches here!!");
}

此代码仅在延迟后为代码使用线程池线程,并在task.Wait()调用时重新引发异常 - 当然,您可能想要执行其他操作。

作为旁注,即使您不想明确等待任务完成,也不应该使用while (true) {}来阻止应用程序终止 - 一个简单的{{ 1}}也可以正常工作,并且不会将你的一个CPU核心推向100%利用率:)

答案 2 :(得分:1)

我在这里修改了你的问题以捕捉异常。

    static void Main(string[] args)
    {
        DoFoo();
        Console.ReadKey();
    }



    static async void DoFoo()
    {
        try
        {
            await Foo();
        }
        catch (Exception ex)
        {
            //This is where you can catch your exception
        }
    }




    static async Task Foo()
    {

        await MainTask().ContinueWith((Task someTask) =>
        {

            Console.WriteLine("Main State=" + someTask.Status.ToString() + " IsFaulted=" + someTask.IsFaulted + " isComplete=" + someTask.IsCompleted);

        }, TaskContinuationOptions.NotOnFaulted);

    }

    static async Task MainTask()
    {


        Console.WriteLine("MainStarted!");
        Task someTask = Task.Run(() =>
        {
            Console.WriteLine("SleepStarted!");
            Thread.Sleep(1000);
            Console.WriteLine("SleepEnded!");
        });
        await someTask;
        throw new Exception("CustomException!");

        Console.WriteLine("Waiting Ended!!");


    }

你应该使用TaskContinuationOptions.NotOnFaulted这意味着只有在父任务没有任何例外的情况下才会执行continue with task。