2015-09-04 10 views
15

Mam problem w poniższym kodzie:Dlaczego Zadanie kończy nawet czekają

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

po prostu chcesz uzyskać wyjątek od nowego rozpoczętego zadania MainTask. Ale wynik nie był tym, czego się spodziewałem.

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

Jak widać wynik, zadanie kończy się przed "Oczekiwanie zakończone !!" log konsoli. Nie mam pojęcia, dlaczego MainTask zakończył się, nawet jeśli w MainTask ma await polecenia w środku? Czy coś przeoczyłem?

+0

Dowolny powód, dla którego nie można wykonać 'MainTask().ContinueWith (...) 'bezpośrednio? –

Odpowiedz

18

Task.Factory.StartNewdoes not understand async delegates, więc w tym przypadku należy użyć Task.Run, a wyjątek powinien przepłynąć.

Task.Factory.StartNew(MainTask); 

jest zasadniczo równoważne

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

który ignoruje wracającego zadanie z MainTask i wyjątek tylko zostanie połknięty.

Aby uzyskać więcej informacji, zobacz ten blog post.

Spróbuj użyć Task.Run zamiast a dostaniesz swój wyjątek:

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, Aby ponownie sformułować, że: "TaskFactory.StartNew nie obsługuje async-świadomy delegatów." Task.Run ma. " Zobacz ten [blog] (http://blog.stephencleary.com/2015/03/a-tour-of-task-part-9-delegate-tasks.html). –

+0

Być może powinienem powiedzieć, że nie rozumie delegatów async. Zobacz http://blog.stephencleary.com/2013/08/startnew-is-dangerous.html –

1

zmodyfikowałem swój problem tutaj, aby złapać wyjątki.

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


    } 

należy użyć TaskContinuationOptions.NotOnFaulted co oznacza, że ​​nadal z zadaniem będzie tylko wykonać zadanie, jeśli rodzic nie miał żadnych wyjątków.

6

Tu są świetne odpowiedzi, ale chciałbym zwrócić uwagę na oczywiste - Task.Factory.StartNew jest całkowicie zbędne, niepotrzebne i używane źle.

Jeśli zastąpić

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

z

Task newTask = MainTask(); 

Dostaniesz dokładnie zachowanie się było spodziewać, nie tracąc jeszcze inny wątek puli wątków tylko do rozpoczęcia inny wątek puli wątków. W rzeczywistości, jeśli chciała przepisać przykład być bardziej idiomatyczne, można użyć czegoś takiego:

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

Ten kod tylko używa wątku puli wątków dla kodu z opóźnieniem, i ponownie generuje wyjątek na wywołanie task.Wait() - oczywiście możesz chcieć zrobić coś innego.

Na marginesie, nawet jeśli nie chcesz jawnie czekać na zakończenie zadania, nie powinieneś używać while (true) {}, aby uniemożliwić zakończenie aplikacji - prosty Console.ReadLine() zadziała równie dobrze, a isn wepchnie jeden z rdzeni procesora do 100% wykorzystania :)

Powiązane problemy