epplus - Java Equivalent C#async / await?




read rows (13)

Я обычный разработчик C #, но иногда я разрабатываю приложение в Java. Я ищу, есть ли какой-либо Java-эквивалент C # async / wait? Простыми словами, что является эквивалентом java,

async Task<int> AccessTheWebAsync()
{ 
    HttpClient client = new HttpClient();
    var urlContents = await client.GetStringAsync("http://msdn.microsoft.com");
    return urlContents.Length;
}

Answers

К сожалению, Java не имеет эквивалента async / await. Самое близкое, что вы можете получить, вероятно, связано с ListenableFuture от Guava и цепочкой прослушивателей, но было бы очень тяжело писать для случаев, связанных с несколькими асинхронными вызовами, так как уровень вложенности очень быстро увеличивался.

Если вы согласны с использованием другого языка поверх JVM, к счастью, в Scala есть async / await, который является прямым эквивалентом async / await C # с почти идентичным синтаксисом и семантикой: https://github.com/scala/async/

Обратите внимание: хотя эта функциональность требовала довольно продвинутой поддержки компилятора на C #, в Scala она могла быть добавлена ​​в библиотеку благодаря очень мощной макросистеме в Scala и поэтому может быть добавлена ​​даже в более старые версии Scala, например, 2.10. Кроме того, Scala совместима со стандартами Java, поэтому вы можете написать асинхронный код в Scala, а затем вызвать его с Java.

Существует также другой подобный проект под названием Akka Dataflow http://doc.akka.io/docs/akka/2.3-M1/scala/dataflow.html который использует разные формулировки, но концептуально очень похож, однако реализован с использованием разграниченных продолжений, а не макросов (поэтому он работает с более старыми версиями Scala, такими как 2.9).


Java имеет класс java.util.concurrent.Future который эквивалентен классу Task C #.

Вы можете запустить работу над объектом java.util.concurrent.Executor . Существует много реализаций, но ForkJoinTask.fork() стоит посмотреть, если вы ForkJoinTask.fork() как только попробуете это.

Когда вы начнете работу, вам дадут Future . Ваш метод продолжит работу. Когда вам нужен результат из вашего будущего, вы вызываете get() и он будет блокироваться до тех пор, пока результат не будет готов. Это похоже на использование ключевого слова await в C #.


await использует продолжение для выполнения дополнительного кода при завершении асинхронной операции ( client.GetStringAsync(...) ).

Таким образом, в качестве самого близкого приближения я использовал бы приложение CompletableFuture<T> (эквивалентное Java 8 для .NET- Task<TResult> ) для обработки запроса Http асинхронно.

ОБНОВЛЕНО 25-05-2016 к AsyncHttpClient v.2, выпущенному на Abril 13th 2016:

Таким образом, Java 8, эквивалентный OP-примеру AccessTheWebAsync() выглядит следующим образом:

CompletableFuture<Integer> AccessTheWebAsync()
{
    AsyncHttpClient asyncHttpClient = new DefaultAsyncHttpClient();
    return asyncHttpClient
       .prepareGet("http://msdn.microsoft.com")
       .execute()
       .toCompletableFuture()
       .thenApply(Response::getResponseBody)
       .thenApply(String::length);
}

Это использование было взято из ответа на вопрос: Как мне получить CompletableFuture из запроса клиента Async Http? и который соответствует новому API, представленному в версии 2 AsyncHttpClient выпущенному на Abril 13th 2016, который уже имеет встроенную поддержку CompletableFuture<T> .

Исходный ответ с использованием версии 1 AsyncHttpClient:

Для этого у нас есть два возможных подхода:

  • первый использует неблокирующий IO, и я называю его AccessTheWebAsyncNio . Тем не менее, поскольку AsyncCompletionHandler является абстрактным классом (вместо функционального интерфейса), мы не можем передавать лямбда в качестве аргумента. Таким образом, это связано с неизбежной многословностью из-за синтаксиса анонимных классов. Однако это решение наиболее близко к потоку выполнения данного примера C # .

  • второй - немного менее подробный, однако он отправит новую задачу, которая в конечном итоге заблокирует поток на f.get() пока ответ не будет завершен.

Первый подход , более подробный, но не блокирующий:

static CompletableFuture<Integer> AccessTheWebAsyncNio(){
    final AsyncHttpClient asyncHttpClient = new AsyncHttpClient();
    final CompletableFuture<Integer> promise = new CompletableFuture<>();
    asyncHttpClient
        .prepareGet("https://msdn.microsoft.com")
        .execute(new AsyncCompletionHandler<Response>(){
            @Override
            public Response onCompleted(Response resp) throws Exception {
                promise.complete(resp.getResponseBody().length());
                return resp;
            }
        });
    return promise;
}

Второй подход менее подробный, но блокирующий поток:

static CompletableFuture<Integer> AccessTheWebAsync(){
    try(AsyncHttpClient asyncHttpClient = new AsyncHttpClient()){
        Future<Response> f = asyncHttpClient
            .prepareGet("https://msdn.microsoft.com")
            .execute();
        return CompletableFuture.supplyAsync(
            () -> return f.join().getResponseBody().length());
    }
}

Если вы просто после чистого кода, который имитирует тот же эффект, что и async / await в java, и не против блокировать поток, который он вызывается до тех пор, пока он не будет завершен, например, в тесте, вы можете использовать что-то вроде этого кода:

interface Async {
    void run(Runnable handler);
}

static void await(Async async) throws InterruptedException {

    final CountDownLatch countDownLatch = new CountDownLatch(1);
    async.run(new Runnable() {

        @Override
        public void run() {
            countDownLatch.countDown();
        }
    });
    countDownLatch.await(YOUR_TIMEOUT_VALUE_IN_SECONDS, TimeUnit.SECONDS);
}

    await(new Async() {
        @Override
        public void run(final Runnable handler) {
            yourAsyncMethod(new CompletionHandler() {

                @Override
                public void completion() {
                    handler.run();
                }
            });
        }
    });

Как уже упоминалось, прямого эквивалента нет, но очень близкое приближение может быть создано с помощью модификаций байт-кода Java (для команд async / await-like и реализации последующих продолжений).

Я сейчас работаю над проектом, который реализует async / wait поверх библиотеки продолжения JavaFlow , проверьте https://github.com/vsilaev/java-async-await

Пока нет Maven mojo, но вы можете запускать примеры с предоставленным Java-агентом. Вот как выглядит код async / await:

public class AsyncAwaitNioFileChannelDemo {

public static void main(final String[] argv) throws Exception {

    ...
    final AsyncAwaitNioFileChannelDemo demo = new AsyncAwaitNioFileChannelDemo();
    final CompletionStage<String> result = demo.processFile("./.project");
    System.out.println("Returned to caller " + LocalTime.now());
    ...
}


public @async CompletionStage<String> processFile(final String fileName) throws IOException {
    final Path path = Paths.get(new File(fileName).toURI());
    try (
            final AsyncFileChannel file = new AsyncFileChannel(
                path, Collections.singleton(StandardOpenOption.READ), null
            );              
            final FileLock lock = await(file.lockAll(true))
        ) {

        System.out.println("In process, shared lock: " + lock);
        final ByteBuffer buffer = ByteBuffer.allocateDirect((int)file.size());

        await( file.read(buffer, 0L) );
        System.out.println("In process, bytes read: " + buffer);
        buffer.rewind();

        final String result = processBytes(buffer);

        return asyncResult(result);

    } catch (final IOException ex) {
        ex.printStackTrace(System.out);
        throw ex;
    }
}

@async - это аннотация, которая помещает метод как асинхронно исполняемый, await () - это функция, которая ждет на CompletableFuture с продолжением, а вызов «return asyncResult (someValue)» - это то, что завершает ассоциированный CompletableFuture / Continuation

Как и в случае с C #, поток управления сохраняется, и обработка исключений может выполняться обычным образом (try / catch, как в последовательно исполняемом коде)


Нет, нет эквивалента async / await в Java - или даже в C # перед v5.

Это довольно сложная языковая функция для создания конечной машины за кулисами.

В Java существует относительно небольшая языковая поддержка для асинхронности / параллелизма, но пакет java.util.concurrent содержит много полезных классов . (Не совсем эквивалентно параллельной библиотеке задач, но ближе всего к ней).


C # async / await сопоставим с понятием, известным как Fibers или кооперативные потоки, или легкие потоки . Нет эквивалента C # async / await в Java на уровне языка, но вы можете найти библиотеки, поддерживающие волокна.

Java-библиотеки, реализующие волокна

Вы можете прочитать эту статью (от Quasar) за хорошее введение в волокна. Он охватывает те потоки, как волокна могут быть реализованы на JVM и имеет определенный код Quasar.


AsynHelper Java AsynHelper включает в себя набор полезных классов / методов для таких асинхронных вызовов (и ожидания).

Если желательно выполнить набор вызовов методов или кодовых блоков асинхронно, он включает полезный вспомогательный метод AsyncTask .submitTasks, как показано ниже.

AsyncTask.submitTasks(
    () -> getMethodParam1(arg1, arg2),
    () -> getMethodParam2(arg2, arg3)
    () -> getMethodParam3(arg3, arg4),
    () -> {
             //Some other code to run asynchronously
          }
    );

Если требуется дождаться завершения всех асинхронных кодов, можно использовать переменную AsyncTask.submitTasksAndWait .

Кроме того, если требуется получить возвращаемое значение из каждого вызова асинхронного метода или кодового блока, можно использовать AsyncSupplier .submitSuppliers , чтобы затем получить результат из массива поставщиков результатов, возвращаемого методом. Ниже приведен пример фрагмента:

Supplier<Object>[] resultSuppliers = 
   AsyncSupplier.submitSuppliers(
     () -> getMethodParam1(arg1, arg2),
     () -> getMethodParam2(arg3, arg4),
     () -> getMethodParam3(arg5, arg6)
   );

Object a = resultSuppliers[0].get();
Object b = resultSuppliers[1].get();
Object c = resultSuppliers[2].get();

myBigMethod(a,b,c);

Если тип возврата каждого метода отличается, используйте приведенный ниже фрагмент.

Supplier<String> aResultSupplier = AsyncSupplier.submitSupplier(() -> getMethodParam1(arg1, arg2));
Supplier<Integer> bResultSupplier = AsyncSupplier.submitSupplier(() -> getMethodParam2(arg3, arg4));
Supplier<Object> cResultSupplier = AsyncSupplier.submitSupplier(() -> getMethodParam3(arg5, arg6));

myBigMethod(aResultSupplier.get(), bResultSupplier.get(), cResultSupplier.get());

Результат асинхронных вызовов / кодовых блоков метода также может быть получен в другой точке кода в том же потоке или другом потоке, что и в приведенном ниже фрагменте.

AsyncSupplier.submitSupplierForSingleAccess(() -> getMethodParam1(arg1, arg2), "a");
AsyncSupplier.submitSupplierForSingleAccess(() -> getMethodParam2(arg3, arg4), "b");
AsyncSupplier.submitSupplierForSingleAccess(() -> getMethodParam3(arg5, arg6), "c");


//Following can be in the same thread or a different thread
Optional<String> aResult = AsyncSupplier.waitAndGetFromSupplier(String.class, "a");
Optional<Integer> bResult = AsyncSupplier.waitAndGetFromSupplier(Integer.class, "b");
Optional<Object> cResult = AsyncSupplier.waitAndGetFromSupplier(Object.class, "c");

 myBigMethod(aResult.get(),bResult.get(),cResult.get());

Сама Java не имеет эквивалентных функций, но существуют сторонние библиотеки, которые предлагают аналогичную функциональность, например Kilim .


Во-первых, поймите, что такое async / await. Это способ однопоточного приложения GUI или эффективного сервера для запуска нескольких «волокон» или «со-подпрограмм» или «легких потоков» в одном потоке.

Если вы используете обычные потоки, то эквивалент Java - это ExecutorService.submit и Future.get . Это будет блокироваться до завершения задачи и вернуть результат. Между тем, другие потоки могут работать.

Если вы хотите получить что-то вроде волокон, вам нужна поддержка в контейнере (я имею в виду в цикле событий GUI или в обработчике запросов HTTP-сервера), или напишите свой собственный. Например, Servlet 3.0 предлагает асинхронную обработку. JavaFX предлагает javafx.concurrent.Task . Тем не менее, у них нет элегантности языковых функций. Они работают через обычные обратные вызовы.


Я создаю и освобождаю Java async / wait. https://github.com/stofu1234/kamaitachi

Эта библиотека не нуждается в расширении компилятора и реализует многоуровневую обработку ввода-вывода в Java.

    async Task<int> AccessTheWebAsync(){ 
        HttpClient client= new HttpClient();
        var urlContents= await client.GetStringAsync("http://msdn.microsoft.com");
       return urlContents.Length;
    }

    //LikeWebApplicationTester.java
    BlockingQueue<Integer> AccessTheWebAsync() {
       HttpClient client = new HttpClient();
       return awaiter.await(
            () -> client.GetStringAsync("http://msdn.microsoft.com"),
            urlContents -> {
                return urlContents.length();
            });
    }
    public void doget(){
        BlockingQueue<Integer> lengthQueue=AccessTheWebAsync();
        awaiter.awaitVoid(()->lengthQueue.take(),
            length->{
                System.out.println("Length:"+length);
            }
            );
    }

Проверьте ea-async который переписывает байт-код Java, чтобы имитировать async / wait довольно красиво. По их readme: «Это сильно вдохновлено Async-Await в .NET CLR»


В моем случае мне нужно было вернуть перечисление из службы WCF. Мне также понадобилось дружеское имя, а не только enum.ToString ().

Вот мой класс WCF.

[DataContract]
public class EnumMember
{
    [DataMember]
    public string Description { get; set; }

    [DataMember]
    public int Value { get; set; }

    public static List<EnumMember> ConvertToList<T>()
    {
        Type type = typeof(T);

        if (!type.IsEnum)
        {
            throw new ArgumentException("T must be of type enumeration.");
        }

        var members = new List<EnumMember>();

        foreach (string item in System.Enum.GetNames(type))
        {
            var enumType = System.Enum.Parse(type, item);

            members.Add(
                new EnumMember() { Description = enumType.GetDescriptionValue(), Value = ((IConvertible)enumType).ToInt32(null) });
        }

        return members;
    }
}

Вот метод расширения, который получает описание из Enum.

    public static string GetDescriptionValue<T>(this T source)
    {
        FieldInfo fileInfo = source.GetType().GetField(source.ToString());
        DescriptionAttribute[] attributes = (DescriptionAttribute[])fileInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);            

        if (attributes != null && attributes.Length > 0)
        {
            return attributes[0].Description;
        }
        else
        {
            return source.ToString();
        }
    }

Реализация:

return EnumMember.ConvertToList<YourType>();




c# java