Myślę, że tutaj wprowadzasz kilka rzeczy w błąd. Co pytasz, jest już możliwe przy użyciu System.Threading.Tasks
The async
a await
w C # 5 są właśnie zamierza dostarczyć trochę ładniejszy cukier syntaktyczny dla tej samej funkcji.
Użyjmy przykładu Winforms - upuść przycisk i pole tekstowe na formularzu i użyj tego kodu:
private void button1_Click(object sender, EventArgs e)
{
Task.Factory.StartNew<int>(() => DelayedAdd(5, 10))
.ContinueWith(t => DelayedAdd(t.Result, 20))
.ContinueWith(t => DelayedAdd(t.Result, 30))
.ContinueWith(t => DelayedAdd(t.Result, 50))
.ContinueWith(t => textBox1.Text = t.Result.ToString(),
TaskScheduler.FromCurrentSynchronizationContext());
}
private int DelayedAdd(int a, int b)
{
Thread.Sleep(500);
return a + b;
}
Uruchom go, a zobaczysz, że (a) nie blokuje wątku interfejsu użytkownika i (b) nie pojawia się zwykły błąd „niepoprawna operacja wielowątkowa” - chyba że usuniesz TaskScheduler
argument z ostatniego ContinueWith
, w w którym przypadku będziesz.
Jest to standardowy sposób przekazywania kontynuacji . Magia dzieje się w TaskScheduler
klasie, a konkretnie w instancji, którą odzyskuje FromCurrentSynchronizationContext
. Przekaż to do dowolnej kontynuacji, a powiesz, że kontynuacja musi działać na dowolnym wątku o nazwie FromCurrentSynchronizationContext
metoda - w tym przypadku na wątku interfejsu użytkownika.
Oczekujący są nieco bardziej wyrafinowani w tym sensie, że są świadomi, w którym wątku rozpoczęli i w którym wątku musi nastąpić kontynuacja. Tak więc powyższy kod można napisać nieco bardziej naturalnie:
private async void button1_Click(object sender, EventArgs e)
{
int a = await DelayedAddAsync(5, 10);
int b = await DelayedAddAsync(a, 20);
int c = await DelayedAddAsync(b, 30);
int d = await DelayedAddAsync(c, 50);
textBox1.Text = d.ToString();
}
private async Task<int> DelayedAddAsync(int a, int b)
{
Thread.Sleep(500);
return a + b;
}
Te dwa powinny wyglądać bardzo podobnie, a w rzeczywistości są bardzo podobne. DelayedAddAsync
Metoda teraz zwraca Task<int>
zamiast int
, a więc await
jest tylko bicie kontynuacje na każdym z nich. Główną różnicą jest to, że przechodzi przez kontekst synchronizacji w każdej linii, więc nie musisz robić tego wyraźnie, jak to zrobiliśmy w poprzednim przykładzie.
Teoretycznie różnice są znacznie bardziej znaczące. W drugim przykładzie każda pojedyncza linia w button1_Click
metodzie jest faktycznie wykonywana w wątku interfejsu użytkownika, ale samo zadanie ( DelayedAddAsync
) działa w tle. W pierwszym przykładzie wszystko działa w tle , z wyjątkiem przypisania, do textBox1.Text
którego wyraźnie przypisaliśmy kontekst synchronizacji wątku interfejsu użytkownika.
To jest naprawdę interesujące await
- fakt, że oczekujący jest w stanie wskoczyć i wyjść z tej samej metody bez blokowania wywołań. Dzwonisz await
, bieżący wątek wraca do przetwarzania wiadomości, a kiedy to się skończy, oczekujący wybierze dokładnie tam, gdzie go przerwał, w tym samym wątku, w którym przerwał. Ale jeśli chodzi o twój Invoke
/ BeginInvoke
kontrast w pytaniu, ja ' Przykro mi to mówić, że powinieneś przestać to robić dawno temu.
await
funkcjonalności. To tylko dużo cukru syntaktycznego do kontynuacji . Być może istnieją inne niepowiązane ulepszenia WinForm, które powinny pomóc? To byłoby objęte samą platformą .NET, a nie C #.