2017-01-22 31 views
5

Rozważmy następujący fragment kodu:Dlaczego Task.Factory.StartNew zwraca natychmiast, gdy Task.Run nie?

Task[] tasks = new Task[4]; 
for (var i = 0; i < tasks.Length; ++i) { 
    tasks[i] = Task.Run(async() => 
    { 
     await Task.Delay(4000); 
    }); 
} 
for (var i = 0; i < tasks.Length; ++i) 
    await tasks[i]; 

Console.WriteLine("Done!"); 

To działa zgodnie z oczekiwaniami, biorąc 4.000 ms do wykonania. Jeśli jednak wymieniam Task.Run z Task.Factory.StartNew, zajmie to tylko 0,006 ms!

Czy ktoś może wyjaśnić, dlaczego?

Odpowiedz

3

Ok, znalazłem odpowiedź sobie po przeczytaniu tego https://blogs.msdn.microsoft.com/pfxteam/2011/10/24/task-run-vs-task-factory-startnew/

Używając słowa kluczowego asynchronicznej tutaj, kompilator będzie mapować tej delegata być Func<Task<int>>: wywoływanie delegata powróci Task<int> do reprezentowania ewentualnego zakończenia tego połączenia. I od delegata to Func<Task<int>>, TResult to Task<int>, a tym samym typ "t" ma być Task<Task<int>>, a nie Task<int>.

Więc ten kod działa zgodnie z oczekiwaniami:

Task[] tasks = new Task[4]; 
for (var i = 0; i < tasks.Length; ++i) { 
    tasks[i] = Task.Factory.StartNew(async() => 
    { 
     await Task.Delay(4000); 
    }); 
} 
for (var i = 0; i < tasks.Length; ++i) 
    await await (tasks[i] as Task<Task>); 

Console.WriteLine("Done!"); 

które mogą być realizowane również za pomocą Unwrap:

Task[] tasks = new Task[4]; 
for (var i = 0; i < tasks.Length; ++i) { 
    tasks[i] = Task.Factory.StartNew(async() => 
    { 
     await Task.Delay(4000); 
    }).Unwrap(); 
} 
for (var i = 0; i < tasks.Length; ++i) 
    await tasks[i]; 

Console.WriteLine("Done!"); 
3

Odpowiedź jest here

istnieje różnica w zachowanie między dwiema metodami w odniesieniu do: Task.Run (Działanie) domyślnie nie zezwala zadania dziecko zaczęło się opcję TaskCreationOptions.AttachedToParent dołączyć do bieżącej instancji zadań, natomiast StartNew (Działanie) nie

Tak, Task.Run będzie zaczekać wykonanie zostanie zakończone, a Task.Factory.StartNew natychmiast zwróci zadanie.

5

Czy ktoś może wyjaśnić dlaczego?

Mówiąc prościej, StartNew nie rozumie async delegatów.

Tak więc when your delegate returns an incomplete task at its first await, StartNew widzi wyjście delegata i uważa, że ​​jego praca została ukończona. Dlatego zwraca tutaj Task<Task>. Task.Run ma specjalną logikę do obsługi asynchronicznych delegatów, automatycznie rozpakowując wewnętrzne zadanie.

To tylko jedna z pułapek używania StartNew z asynchronicznym kodem; Szczegółowo omawiam to i inne w moim poście na blogu StartNew is Dangerous.