Лямбда с побочным эффектом
От: f95.2  
Дата: 09.05.20 12:43
Оценка:
Насколько я знаю, в java нет методаожидания с предикатом, аналога std::condition_variable::wait из c++.

Допустим, я написал такой метсод с сигнатурой:
public void TimedWait(Object monitor, long timeout_msec, BooleanSupplier condition);


Далее, допустим, предикат внутри себя вычисляет какое-то значение, если оно не равно плохому, то возвращает true (т.е. необходимость остановить ожидание).
{
    ValueType value = calculateValue();
    return value.equals(badValue);
}


Предикат хочется сделать лямбдой, т.к. он маленький.

Допуcтим также, что, пока значение равно плохому, то calculateValue отрабатывает нормально и быстро, но после того, как будет получено нужное значение,
данные, из которых оно вычислено, необратимо меняются, и снова значение не получить.

Ну и наконец, допустим, что я хочу получить это значение после того, как отработал метод TimedWait.
Вопрос: как это сделать?

Допущения проистекают от дерьмового дизайна системы, и я этот дизайн переделаю, но остается чисто академический интерес.

А проблема вытащить занчение в том, что в лямбду можно передавать только константные переменные,
так что через замыкание вернуть значение красиво нельзя.

Варианты, которые вижу я:
1. Сделать какую-то обертку над значением, например, массив из одного элемента, или специальный класс,
его передать в предикат (ссылка на эту обертку будет константной), и в предикате выставить значение.

Т.е. как-то так:
ValueType[] wrapper = new ValueType[1];

TimedWait(monitor, timeout_msec, () -> {
    wrapper[0] = calculateValue();
    return !wrapper[0].equals(ифвValue);
});


Недостаток — выглядит криво.

2. Сделать предикат не лямбдой, а полноценным объектом спец. класса, значение передавать через поле класса.
Недостаток — лишний класс, слишком много лишнего кода.

3. Сделать доп. функцию (рядом с calculateValue), которая где-то сохраняет переданное значение, и вызывать ее внутри предиката.
Недостаток — лишняя функция, лишнее поле класса. Криво, в общем.

Мб есть какой-то красивый и идиоматичный способ решить подобную проблему.
Или вообще отказаться от лямбды с побочным эффектом, а сделать как-то еще?
Re: Лямбда с побочным эффектом
От: GarryIV  
Дата: 09.05.20 16:07
Оценка:
Здравствуйте, f95.2, Вы писали:

F2>Насколько я знаю, в java нет методаожидания с предикатом, аналога std::condition_variable::wait из c++.


F2>Допустим, я написал такой метсод с сигнатурой:

F2>
F2>public void TimedWait(Object monitor, long timeout_msec, BooleanSupplier condition);
F2>

С большой буквы имя класса пишется, имя метода с маленькой.

F2>TimedWait(monitor, timeout_msec, () -> {

F2> wrapper[0] = calculateValue();
F2> return !wrapper[0].equals(ифвValue);
F2>});
F2>[/java]

F2>Мб есть какой-то красивый и идиоматичный способ решить подобную проблему.

F2>Или вообще отказаться от лямбды с побочным эффектом, а сделать как-то еще?

Пусть TimedWait возвращает значение. Зачем эти грязные функции на ровном месте? И кондишн у тебя вот ни разу не кондишн а 2 в 1, ValueSupplier + ValuePredicаte. Это можно причесать если лямбду сделать типа () -> Optional<T>.

Типа так
<T> T timedWait(Object monitor, int timeoutMsec, Supplier<Optional<T>> supplier) {
...
}


Использовать так:
ValueType value = timedWait(monitor, timeoutMsec, () -> Optional.of(calculateValue()).filter(it -> !it.equals(badValue)));


Или сделать 2 отдельных параметра.

PS A вообще я бы прикрутил CompletableFuture раз там у тебя многопоточность какая-то. Чтоб calculateValue прямо ее и возвращал.
WBR, Igor Evgrafov
Отредактировано 09.05.2020 16:14 GarryIV . Предыдущая версия .
Re: Лямбда с побочным эффектом
От: Слава  
Дата: 09.05.20 16:23
Оценка:
Здравствуйте, f95.2, Вы писали:

F2>Насколько я знаю, в java нет методаожидания с предикатом, аналога std::condition_variable::wait из c++.


https://stackoverflow.com/questions/1643141/what-is-a-condition-variable-in-java

https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/Condition.html

Не оно ли?

PS: насколько я знаю, С++ может сделать только то, что ему позволяет сделать операционная система, с её тредами и системными вызовами синхронизации. То же относится и к Яве.
Re: Лямбда с побочным эффектом
От: · Великобритания  
Дата: 09.05.20 18:07
Оценка: +1
Здравствуйте, f95.2, Вы писали:

f> Насколько я знаю, в java нет методаожидания с предикатом, аналога std::condition_variable::wait из c++.

Есть java.util.concurrent.locks.Condition#await
Предикат непонятно зачем нужен. Просто условие помещаешь в обычный while-цикл.


f> Допуcтим также, что, пока значение равно плохому, то calculateValue отрабатывает нормально и быстро, но после того, как будет получено нужное значение,

f> данные, из которых оно вычислено, необратимо меняются, и снова значение не получить.
Если я правильно телепатировал твою задачу, то тебе подойдёт CompletableFuture
avalon/2.0.6
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re: Лямбда с побочным эффектом
От: StanislavK Великобритания  
Дата: 11.05.20 09:26
Оценка:
Здравствуйте, f95.2, Вы писали:


F2>Ну и наконец, допустим, что я хочу получить это значение после того, как отработал метод TimedWait.

F2>Вопрос: как это сделать?

Как-то так не подходит?
Executor executor = Executors.newSingleThreadExecutor();
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> calculateValue(), executor);
try {
    String value = future.get(10, TimeUnit.SECONDS);
    System.out.println(value);
} catch (TimeoutException e) {
    System.out.println("timeout");
}

// value after first timeout
String value = future.get(10, TimeUnit.SECONDS);
Re[2]: Лямбда с побочным эффектом
От: f95.2  
Дата: 11.05.20 12:31
Оценка:
Здравствуйте, StanislavK, Вы писали:

SK>Как-то так не подходит?

А ты не мог бы объяснить, что тут происходит? Я что-то в экзекуторы не въезжаю пока,
а в интернете как-то уж слишком поверхностно описывают.

Я правильно понимаю, что любой Executor — это по сути обертка над пулом потоков и очередью задач?
Если да, то не слишком ли жирно поднимать такую конструкцию для простой задачи ожидания на условной переменной?

SK>
SK>Executor executor = Executors.newSingleThreadExecutor();
SK>

Этот новый экзекутор будет выполнять задачи в новом потоке, или в вызывающем, когда вызывающий
заблокируется на асинхронной операции?

SK>CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> calculateValue(), executor);

Есть еще метод CompletableFuture.supplyAsync, который не принимает executor, а использует ForkJoinPool.commonPool().
Почему ты не стал использовать его, в чем разница с твоим подходом?
Re[2]: Лямбда с побочным эффектом
От: f95.2  
Дата: 11.05.20 12:33
Оценка:
Здравствуйте, GarryIV, Вы писали:

GIV>С большой буквы имя класса пишется, имя метода с маленькой.

Это твои личные заморочки, или общая религия всея Java?

GIV>Пусть TimedWait возвращает значение. Зачем эти грязные функции на ровном месте? И кондишн у тебя вот ни разу не кондишн а 2 в 1, ValueSupplier + ValuePredicаte. Это можно причесать если лямбду сделать типа () -> Optional<T>.


Попробую так, спасибо.
Re[3]: Лямбда с побочным эффектом
От: StanislavK Великобритания  
Дата: 11.05.20 13:46
Оценка: +1
Здравствуйте, f95.2, Вы писали:

F2>Я правильно понимаю, что любой Executor — это по сути обертка над пулом потоков и очередью задач?

Ну да, так и есть.

F2>Если да, то не слишком ли жирно поднимать такую конструкцию для простой задачи ожидания на условной переменной?

Жирно конечно. Надо завести его где-нить и переиспользовать.

SK>>
SK>>Executor executor = Executors.newSingleThreadExecutor();
SK>>

F2>Этот новый экзекутор будет выполнять задачи в новом потоке, или в вызывающем, когда вызывающий
F2>заблокируется на асинхронной операции?
Этот — в отдельном потоке.

SK>>CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> calculateValue(), executor);

F2>Есть еще метод CompletableFuture.supplyAsync, который не принимает executor, а использует ForkJoinPool.commonPool().
F2>Почему ты не стал использовать его, в чем разница с твоим подходом?
Потому, что common pool еще кто-то где-то использует и кто этот кто-то и как он его использует — никто не знает. Поэтому лучше создать свой пул.
Если честно, то использование дефолтного пула подходит только для hello world.
Re[3]: Лямбда с побочным эффектом
От: GarryIV  
Дата: 11.05.20 15:18
Оценка:
Здравствуйте, f95.2, Вы писали:

GIV>>С большой буквы имя класса пишется, имя метода с маленькой.

F2>Это твои личные заморочки, или общая религия всея Java?
Это стандарт https://www.oracle.com/java/technologies/javase/codeconventions-namingconventions.html

бывают разные отклонения но имена методов и классов практически на 100% соблюдаются всеми.

Более подробные более-менее общепринятые правила https://google.github.io/styleguide/javaguide.html
Проверять можно этим https://checkstyle.sourceforge.io/
WBR, Igor Evgrafov
Отредактировано 11.05.2020 15:21 GarryIV . Предыдущая версия .
Re[3]: Лямбда с побочным эффектом
От: GarryIV  
Дата: 11.05.20 15:27
Оценка:
Здравствуйте, f95.2, Вы писали:

F2>Я правильно понимаю, что любой Executor — это по сути обертка над пулом потоков и очередью задач?

F2>Если да, то не слишком ли жирно поднимать такую конструкцию для простой задачи ожидания на условной переменной?

У тебя уже есть другой поток, который и готовит данные, нет так ли? Вот оттуда и должо торчать Future. А так да смысла нет поднимать еще поток чисто чтобы подождать.
WBR, Igor Evgrafov
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.