【问题标题】:Why Task finishes even in await为什么任务即使在等待中也能完成
【发布时间】:2015-11-30 04:07:12
【问题描述】:

以下代码有问题:

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(...)

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


【解决方案1】:

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!!");
}

【讨论】:

【解决方案2】:

这里有很好的答案,但我想指出一个显而易见的问题 - 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% 利用率:)

【讨论】:

    【解决方案3】:

    我在这里修改了您的问题以捕获异常。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-05-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多