Различия между С# async и Java ExecutorService
У С# есть классная новая функция
public Task<string> async f()
{
string r = LongCompute();
return r;
}
но это не эквивалентно
public Future<String> f() {
return Globals.executorService.submit(new Callable<String>() {
public String call() throws Exception {
String r = longCompute();
return r;
}
});
}
где в Java у вас больше гибкости, чтобы выбрать threadpool, в котором будет выполняться задача.
А что нас ждать? Это эквивалентно простому вызову get
string s = await f();
точно так же, как
String s = f().get();
Есть ли что-то еще для С#, или это действительно просто синтаксический сахар для версии Java? (Я не гуру С#, поэтому мне может что-то не хватает).
Ответы
Ответ 1
Нет, await
не похож на вызов get()
. Там значительно больше.
Когда вы используете выражение await
в С#, компилятор эффективно создает продолжение, так что, если awaitable еще не завершен, метод может немедленно вернуться и продолжить обработку только после его завершения. Продолжение будет выполняться в соответствующем контексте - поэтому, если вы используете поток пользовательского интерфейса перед выражением await
, вы продолжите работу с потоком пользовательского интерфейса после этого, но не блокируете поток пользовательского интерфейса, пока ожидаете результата. Например:
public async void HandleButtonClick(object sender, EventArgs e)
{
// All of this method will run in the UI thread, which it needs
// to as it touches the UI... however, it won't block when it does
// the web operation.
string url = urlTextBox.Text;
WebClient client = new WebClient();
string webText = await client.DownloadStringTaskAsync(url);
// Continuation... automatically called in the UI thread, with appropriate
// context (local variables etc) which we used earlier.
sizeTextBox.Text = string.Format("{0}: {1}", url, webText.Length);
}
В конечном счете все это синтаксический сахар, но гораздо более сложный сахар, чем вы показали.
В Интернете уже есть много подробной информации. Например:
Ответ 2
Джон не объяснил реальную точку.
Java ExecutorService
основан на потоках, а С# await
можно назвать основанием на волокнах.
Оба позволяют многозадачность, которая разбивает вычислительные ресурсы между параллельными функциями (т.е. функциями, которые работают одновременно). Первый вид многозадачности называется упреждающим, а второй - кооперативным. Исторически сложилось так, что упреждающая многозадачность считалась более продвинутой и превосходила кооператив. Действительно, прежде чем превентивная многозадачность стала поддерживаться потребительскими операционными системами, компьютеры действительно сосали. Однако упреждающая многозадачность имеет свои недостатки. Его трудно запрограммировать, и он использует больше памяти.
Основное различие между ними заключается в том, что превентивная многозадачность позволяет среде выполнения (как правило, самой операционной системе) останавливать любую функцию в любое время и запускать другую функцию (и запускать их одновременно на разных ЦП). Между тем, для совместной многозадачности требуется, чтобы работающая функция завершалась или принудительно приостанавливалась. Большинство из нас знакомы с упреждающей многозадачностью в форме многопоточности, а также с тем тщательным программированием, которое с ним связано. Меньше знакомы с совместной многозадачностью, которые в настоящее время часто называют волокнами или сопрограммами (в этом случае они реализованы в пользовательской среде внутри потоков упреждающей ОС).
Во всяком случае, дело в том, что ExecutorService
и await
не сопоставимы напрямую, а await
не превосходит, по существу, многопоточность (за исключением того, что он имеет хороший синтаксический сахар). Причина С# для включения await
(и основывается на совместной многозадачности) заключается в том, что основные инструментальные средства GUI на платформе не предназначены для многопоточности и рефакторинг их для поддержки concurrency будет занимать лодку работы. Совместная многозадачность хорошо работает для пользовательского интерфейса, поскольку большинство обработчиков событий являются короткими и могут выполняться последовательно. await
расширяет концепцию цикла событий, позволяя обработчикам длинных событий приостанавливаться и возобновляться после того, как функция рендеринга получит шанс запустить. Все это происходит в одном потоке на одном ядре процессора.
Там, где они оба находят общую точку зрения, это обе формы многозадачности, а Future.get
и await Task
- обе формы синхронизации.
Как и следовало ожидать, С# не имеет хорошей поддержки потоков и потоков. Аналогично, Java содержит волокна/совлокальные подпрограммы/асинхронные во многих библиотеках, таких как Servlet 3.0 и javafx.concurrent.Task
.
В ответ на Jon Skeet: Continuation (как называется механизм реализации пользовательских реализаций волокон) является нетривиальным, но потоки не менее сложны в их реализации. Возможно, Джон был отброшен, потому что алгоритмы за потоками находятся в ОС, а не в компиляторе или .NET runtime.
Ответ 3
Просто чтобы продлить правильный ответ Джона Скита.
Нет Java-аналога выражений С# ожидания. Hoverer, некоторые фреймворки Java имеют одинаковую функциональность:
Фактически, они генерируют код состояния машины на лету.