Я думаю, вы запутались здесь. То, о чем вы просите, уже возможно с помощью 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 #.