Различия между С# 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 имеют одинаковую функциональность:

Фактически, они генерируют код состояния машины на лету.