为什么即使使用await,任务仍然会完成

14

以下是我的代码,我遇到了一个问题:

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!!

从结果可以看出,在 "Waiting Ended !!" 控制台日志之前,任务就已经完成了。我不知道为什么 MainTask 结束了,即使在 MainTask 中有 await 命令?我错过了什么吗?


你为什么不能直接使用 MainTask().ContinueWith(...) 呢? - Ilian
3个回答

17

Task.Factory.StartNew 不理解异步委托,所以在这种情况下需要使用Task.Run,异常应该会被传递。

Task.Factory.StartNew(MainTask);

基本上等价于

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

忽略了从MainTask返回的任务,异常被吞噬了。
查看此博客文章以获取更多详细信息。
尝试使用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!!");
}

@MickyDuncan,重新表述一下:“TaskFactory.StartNew不支持异步感知委托。Task.Run支持。”请参考这篇博客 - Ilian
也许我应该说它不理解异步委托。请参阅http://blog.stephencleary.com/2013/08/startnew-is-dangerous.html - NeddySpaghetti

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) {}防止应用程序终止 - 简单的Console.ReadLine()同样有效,并且不会将CPU核心推到100%利用率 :)


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,这意味着继续执行的任务只有在父任务没有任何异常时才会执行。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接