private void RunAsync()
{
string param = "Hi";
Task.Run(() => MethodWithParameter(param));
}
private void MethodWithParameter(string param)
{
}
Edytować
Ze względu na popularne zapotrzebowanie muszę zauważyć, że Taskuruchomiony będzie działał równolegle z wątkiem wywołującym. Zakładając, że domyślnie TaskSchedulerbędzie używany .NET ThreadPool. W każdym razie oznacza to, że musisz wziąć pod uwagę wszelkie parametry przekazywane do elementu Taskjako potencjalnie dostępne dla wielu wątków jednocześnie, co czyni je współdzielonymi. Obejmuje to dostęp do nich w wątku wywołującym.
W moim powyższym kodzie sprawa ta jest całkowicie dyskusyjna. Ciągi znaków są niezmienne. Dlatego użyłem ich jako przykładu. Ale powiedz, że nie używasz String...
Jednym z rozwiązań jest użycie asynci await. To domyślnie spowoduje przechwycenie SynchronizationContextwątku wywołującego i utworzy kontynuację dla pozostałej części metody po wywołaniu awaiti dołączeniu go do utworzonego Task. Jeśli ta metoda jest uruchomiona w wątku GUI WinForms, będzie to typ WindowsFormsSynchronizationContext.
Kontynuacja zostanie uruchomiona po wysłaniu z powrotem do przechwyconych SynchronizationContext- ponownie tylko domyślnie. Więc po awaitrozmowie wrócisz do wątku, od którego zacząłeś . Możesz to zmienić na różne sposoby, w szczególności za pomocą ConfigureAwait. W skrócie, reszta tej metody nie będą kontynuowane aż poTask zakończył w innym wątku. Ale wątek wywołujący będzie nadal działał równolegle, ale nie reszta metody.
To oczekiwanie na zakończenie wykonywania pozostałej części metody może być pożądane lub nie. Jeśli nic w tej metodzie później nie uzyskuje dostępu do parametrów przekazanych do Task, możesz w ogóle nie chcieć ich używać await.
A może użyjesz tych parametrów znacznie później w metodzie. Nie ma powodu, by awaitod razu kontynuować pracę. Pamiętaj, że możesz przechowywać Taskzwracaną wartość w zmiennej i awaitna niej później - nawet w ten sam sposób. Na przykład, gdy potrzebujesz bezpiecznego dostępu do przekazanych parametrów po wykonaniu kilku innych czynności. Ponownie, nie musisz awaitwchodzić w Taskprawo, gdy go uruchamiasz.
W każdym razie prostym sposobem na zapewnienie bezpieczeństwa wątku w odniesieniu do parametrów przekazywanych do Task.Runjest wykonanie tego:
Najpierw trzeba ozdobić RunAsyncz async:
private async void RunAsync()
Ważna uwaga
Najlepiej, aby oznaczona metoda nie zwracała wartości void, jak wspomniano w powiązanej dokumentacji. Typowym wyjątkiem są programy obsługi zdarzeń, takie jak kliknięcia przycisków i tym podobne. Muszą wrócić nieważne. W przeciwnym razie zawsze staram się zwrócić lub podczas używania . Jest to dobra praktyka z kilku powodów.async TaskTask<TResult>async
Teraz możesz awaituruchomić Taskjak poniżej. Nie możesz używać awaitbez async.
await Task.Run(() => MethodWithParameter(param));
Tak więc, ogólnie rzecz biorąc, jeśli wykonujesz awaitzadanie, możesz uniknąć traktowania przekazanych w parametrach jako potencjalnie współdzielonego zasobu ze wszystkimi pułapkami modyfikowania czegoś z wielu wątków jednocześnie. Uważaj także na zamknięcia . Nie będę ich szczegółowo omawiać, ale artykuł, do którego prowadzi łącze, świetnie sobie z tym radzi.
Dygresja
Trochę poza tematem, ale bądź ostrożny przy używaniu wszelkiego rodzaju "blokowania" wątku GUI WinForms, ponieważ jest on oznaczony [STAThread]. Używanie w awaitogóle nie blokuje, ale czasami widzę, że jest używane w połączeniu z jakimś rodzajem blokowania.
„Blok” jest w cudzysłowie, ponieważ technicznie nie można zablokować wątku GUI WinForms . Tak, jeśli używasz lockw wątku GUI WinForms, nadal będzie on pompował komunikaty, mimo że myślisz, że jest „zablokowany”. To nie jest.
W bardzo rzadkich przypadkach może to powodować dziwne problemy. Na przykład jeden z powodów, dla których nigdy nie chcesz używać a lockpodczas malowania. Ale to jest marginalna i złożona sprawa; jednak widziałem, że powoduje to szalone problemy. Więc zanotowałem to dla kompletności.