2015-09-04 61 views
15

我有下面的代码有问题:为什么任务完成后,即使在等待

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结束,即使在MainTaskawait命令里面? 我错过了什么吗?

+0

任何你不能做MainTask()的原因。ContinueWith(...)'直接? –

回答

18

Task.Factory.StartNewdoes 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!!"); 
} 
+0

@MickyDuncan,改写:“TaskFactory.StartNew不支持异步感知委托,Task.Run会。”看到这个[博客](http://blog.stephencleary.com/2015/03/a-tour-of-task-part-9-delegate-tasks.html)。 –

+0

也许我应该说它不理解异步代表。请参阅http://blog.stephencleary.com/2013/08/startnew-is-dangerous.html –

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这意味着,与任务继续将只执行如果父任务没有过任何异常。

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()也可以正常工作,并且isn不会推动你的一个CPU核心到100%的利用:)