Я думаю, вы запутались здесь. То, о чем вы просите, уже возможно с помощью System.Threading.Tasks, asyncи awaitв C # 5 просто предоставим немного более приятного синтаксического сахара для той же функции.
Давайте использовать пример Winforms - перетащите кнопку и текстовое поле на форму и используйте этот код:
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;
}
Запустите его, и вы увидите, что (а) он не блокирует поток пользовательского интерфейса и (б) вы не получите обычную ошибку «операция с несколькими потоками не действительна» - если вы не удалите TaskSchedulerаргумент из последнего ContinueWith, в в каком случае вы будете.
Это стандартное продолжение передачи . Магия происходит в TaskSchedulerклассе и, в частности, в экземпляре, полученном с помощью FromCurrentSynchronizationContext. Передайте это в любое продолжение, и вы скажете, что продолжение должно выполняться в том потоке, который вызвал FromCurrentSynchronizationContextметод - в данном случае, в потоке пользовательского интерфейса.
Ожидатели немного более сложные в том смысле, что они знают, с какого потока они начали и с какого потока должно происходить продолжение. Таким образом, приведенный выше код может быть написан немного более естественно:
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;
}
Эти два должны выглядеть очень похожи, и на самом деле они являются очень похожи. DelayedAddAsyncТеперь метод возвращает Task<int>вместо int, и поэтому awaitпросто хлопая продолжения на каждом из них. Основное отличие состоит в том, что он передает контекст синхронизации в каждой строке, поэтому вам не нужно делать это явно, как мы это делали в последнем примере.
Теоретически различия гораздо более значительны. Во втором примере каждая отдельная строка в button1_Clickметоде фактически выполняется в потоке пользовательского интерфейса, но сама задача ( DelayedAddAsync) выполняется в фоновом режиме. В первом примере все работает в фоновом режиме , за исключением присвоения, к textBox1.Textкоторому мы явно привязали контекст синхронизации потока пользовательского интерфейса.
Вот что действительно интересно await- тот факт, что ожидающий может подключаться и выходить из одного и того же метода без каких-либо блокирующих вызовов. Вы звоните await, текущий поток возвращается к обработке сообщений, и когда это будет сделано, официант подберет именно то место, где остановился, в том же потоке, в котором остановился. Но с точки зрения вашего Invoke/ BeginInvokeконтраста в вопросе, я Мне жаль говорить, что вы должны были перестать делать это давным-давно.
awaitфункциональности. Это просто много синтаксического сахара для продолжения прохождения . Возможно, есть какие-то другие не связанные улучшения WinForms, которые должны помочь? Это относится к самой платформе .NET, а не к C #.