W poniższym kodzie, ze względu na interfejs, klasa LazyBar
musi zwrócić zadanie ze swojej metody (i ze względu na argumenty nie można go zmienić). Jeśli LazyBar
implementacja jest niezwykła, ponieważ zdarza się, że działa szybko i synchronicznie - jaki jest najlepszy sposób na zwrócenie zadania braku operacji z metody?
Przeszedłem Task.Delay(0)
poniżej, jednak chciałbym wiedzieć, czy ma to jakieś skutki uboczne wydajności, jeśli funkcja jest często wywoływana (na przykład argumenty, powiedzmy setki razy na sekundę):
- Czy ten cukier składniowy nie rozwija się w coś dużego?
- Czy zaczyna się zapychać pulę wątków mojej aplikacji?
- Czy tasak kompilatora jest wystarczający, aby radzić sobie
Delay(0)
inaczej? - Byłoby
return Task.Run(() => { });
inaczej?
Czy jest lepszy sposób?
using System.Threading.Tasks;
namespace MyAsyncTest
{
internal interface IFooFace
{
Task WillBeLongRunningAsyncInTheMajorityOfImplementations();
}
/// <summary>
/// An implementation, that unlike most cases, will not have a long-running
/// operation in 'WillBeLongRunningAsyncInTheMajorityOfImplementations'
/// </summary>
internal class LazyBar : IFooFace
{
#region IFooFace Members
public Task WillBeLongRunningAsyncInTheMajorityOfImplementations()
{
// First, do something really quick
var x = 1;
// Can't return 'null' here! Does 'Task.Delay(0)' have any performance considerations?
// Is it a real no-op, or if I call this a lot, will it adversely affect the
// underlying thread-pool? Better way?
return Task.Delay(0);
// Any different?
// return Task.Run(() => { });
// If my task returned something, I would do:
// return Task.FromResult<int>(12345);
}
#endregion
}
internal class Program
{
private static void Main(string[] args)
{
Test();
}
private static async void Test()
{
IFooFace foo = FactoryCreate();
await foo.WillBeLongRunningAsyncInTheMajorityOfImplementations();
return;
}
private static IFooFace FactoryCreate()
{
return new LazyBar();
}
}
}
Task.FromResult<object>(null)
.Odpowiedzi:
Korzystanie z
Task.FromResult(0)
lubTask.FromResult<object>(null)
spowoduje mniejsze koszty ogólne niż tworzenieTask
wyrażenia „no-op”. Podczas tworzeniaTask
z wcześniej ustalonym rezultatem nie ma narzutu związanego z planowaniem.Dzisiaj zaleciłbym użycie Task.CompletedTask, aby to osiągnąć.
źródło
return default(YourReturnType);
Task.CompletedTask
może załatwić sprawę! (ale wymaga .net 4.6)Task.FromResult<TResult>
, co zwraca aTask<TResult>
, spełnia typ zwracanego parametruTask
(bez parametrów typu)?Aby dodać do odpowiedzi Reed Copsey na temat używania
Task.FromResult
, możesz jeszcze bardziej zwiększyć wydajność, jeśli buforujesz już ukończone zadanie, ponieważ wszystkie wystąpienia zakończonych zadań są takie same:Dzięki
TaskExtensions.CompletedTask
możesz używać tego samego wystąpienia w całej domenie aplikacji.Najnowszą wersję .NET Framework (v4.6) dodaje tylko, że z
Task.CompletedTask
własności statyczneźródło
public Task WillBeLongRunningAsyncInTheMajorityOfImplementations()
równie dobrzepublic async Task WillBeLongRunningAsyncInTheMajorityOfImplementations()
. Tak więc, możemy alboreturn CompletedTask;
alboawait CompletedTask;
. Co jest bardziej preferowane (może bardziej wydajne lub przystające)?Task.Delay(0)
tak jak w przyjętej odpowiedzi było dobrym podejściem, ponieważ jest to buforowana kopia ukończonejTask
.Począwszy od wersji 4.6 istnieje teraz
Task.CompletedTask
bardziej wyraźny cel, ale nie tylkoTask.Delay(0)
zwraca pojedyncze wystąpienie zapisane w pamięci podręcznej, ale zwraca to samo wystąpienie zapisane w pamięci podręcznej, co robiTask.CompletedTask
.Buforowana charakter nie gwarantuje pozostanie niezmieniony, ale jak optymalizacji zależna od implementacji, które są zależne wyłącznie jako realizacja optymalizacji (to znaczy, że one nadal działać prawidłowo, jeśli realizacja zmieniona na coś, co było nadal aktualne) wykorzystanie
Task.Delay(0)
było lepsze niż zaakceptowana odpowiedź.źródło
Task.CompletedTask
nie mogę go użyć w projekcie PCL, nawet jeśli ustawię wersję .net na 4.6 (profil 7), właśnie przetestowany w VS2017.Task.CompletedTask => Task.Delay(0);
aby to obsługiwać, więc nie nie wiem na pewno z czubka mojej głowy.Ostatnio się z tym spotkałem i otrzymywałem ostrzeżenia / błędy dotyczące nieważności metody.
Zajmujemy się umieszczaniem kompilatora, a to wyjaśnia:
Łączy w sobie najlepsze z dotychczasowych porad. Nie jest wymagana instrukcja return, chyba że faktycznie robisz coś w metodzie.
źródło
public Task MyVoidAsyncMethod() {}
jest całkowicie taki sam jak powyższa metoda. Jeśli istnieje przypadek użycia go w ten sposób, dodaj dodatkowy kod.źródło
Kiedy musisz zwrócić określony typ:
źródło
Wolę
Task completedTask = Task.CompletedTask;
rozwiązanie .Net 4.6, ale innym podejściem jest oznaczenie metody async i return void:Otrzymasz ostrzeżenie (CS1998 - funkcja asynchroniczna bez oczekiwania na wyrażenie), ale w tym kontekście można to bezpiecznie zignorować.
źródło
Jeśli używasz ogólnych, cała odpowiedź da nam błąd kompilacji. Możesz użyć
return default(T);
. Próbka poniżej, aby wyjaśnić dalej.źródło
źródło