Насколько я знаю, в 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. Сделать какую-то обертку над значением, например, массив из одного элемента, или специальный класс,
его передать в предикат (ссылка на эту обертку будет константной), и в предикате выставить значение.
2. Сделать предикат не лямбдой, а полноценным объектом спец. класса, значение передавать через поле класса.
Недостаток — лишний класс, слишком много лишнего кода.
3. Сделать доп. функцию (рядом с calculateValue), которая где-то сохраняет переданное значение, и вызывать ее внутри предиката.
Недостаток — лишняя функция, лишнее поле класса. Криво, в общем.
Мб есть какой-то красивый и идиоматичный способ решить подобную проблему.
Или вообще отказаться от лямбды с побочным эффектом, а сделать как-то еще?
Здравствуйте, 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 прямо ее и возвращал.
PS: насколько я знаю, С++ может сделать только то, что ему позволяет сделать операционная система, с её тредами и системными вызовами синхронизации. То же относится и к Яве.
Здравствуйте, f95.2, Вы писали:
f> Насколько я знаю, в java нет методаожидания с предикатом, аналога std::condition_variable::wait из c++.
Есть java.util.concurrent.locks.Condition#await
Предикат непонятно зачем нужен. Просто условие помещаешь в обычный while-цикл.
f> Допуcтим также, что, пока значение равно плохому, то calculateValue отрабатывает нормально и быстро, но после того, как будет получено нужное значение, f> данные, из которых оно вычислено, необратимо меняются, и снова значение не получить.
Если я правильно телепатировал твою задачу, то тебе подойдёт CompletableFuture
Здравствуйте, StanislavK, Вы писали:
SK>Как-то так не подходит?
А ты не мог бы объяснить, что тут происходит? Я что-то в экзекуторы не въезжаю пока,
а в интернете как-то уж слишком поверхностно описывают.
Я правильно понимаю, что любой Executor — это по сути обертка над пулом потоков и очередью задач?
Если да, то не слишком ли жирно поднимать такую конструкцию для простой задачи ожидания на условной переменной?
SK>
Этот новый экзекутор будет выполнять задачи в новом потоке, или в вызывающем, когда вызывающий
заблокируется на асинхронной операции?
SK>CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> calculateValue(), executor);
Есть еще метод CompletableFuture.supplyAsync, который не принимает executor, а использует ForkJoinPool.commonPool().
Почему ты не стал использовать его, в чем разница с твоим подходом?
Здравствуйте, GarryIV, Вы писали:
GIV>С большой буквы имя класса пишется, имя метода с маленькой.
Это твои личные заморочки, или общая религия всея Java?
GIV>Пусть TimedWait возвращает значение. Зачем эти грязные функции на ровном месте? И кондишн у тебя вот ни разу не кондишн а 2 в 1, ValueSupplier + ValuePredicаte. Это можно причесать если лямбду сделать типа () -> Optional<T>.
Здравствуйте, f95.2, Вы писали:
F2>Я правильно понимаю, что любой Executor — это по сути обертка над пулом потоков и очередью задач?
Ну да, так и есть.
F2>Если да, то не слишком ли жирно поднимать такую конструкцию для простой задачи ожидания на условной переменной?
Жирно конечно. Надо завести его где-нить и переиспользовать.
SK>>
F2>Этот новый экзекутор будет выполнять задачи в новом потоке, или в вызывающем, когда вызывающий F2>заблокируется на асинхронной операции?
Этот — в отдельном потоке.
SK>>CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> calculateValue(), executor); F2>Есть еще метод CompletableFuture.supplyAsync, который не принимает executor, а использует ForkJoinPool.commonPool(). F2>Почему ты не стал использовать его, в чем разница с твоим подходом?
Потому, что common pool еще кто-то где-то использует и кто этот кто-то и как он его использует — никто не знает. Поэтому лучше создать свой пул.
Если честно, то использование дефолтного пула подходит только для hello world.
Здравствуйте, f95.2, Вы писали:
F2>Я правильно понимаю, что любой Executor — это по сути обертка над пулом потоков и очередью задач? F2>Если да, то не слишком ли жирно поднимать такую конструкцию для простой задачи ожидания на условной переменной?
У тебя уже есть другой поток, который и готовит данные, нет так ли? Вот оттуда и должо торчать Future. А так да смысла нет поднимать еще поток чисто чтобы подождать.