Лучший способ синхронизации асинхронной задачи

Моя проблема: у меня 802.15.4 Беспроводная сеть подключена к последовательному порту (с использованием обертки). Я могу отправлять пакеты в сеть и слушать входящие пакеты. Как вы можете себе представить, это очень асинхронно.

Вот задача: я хочу отправить команды в сеть и дождаться ответа в одном вызове функции. Например: я хочу получить температуру от Node с идентификатором сети 1338.

double getTemperature(int id) throws Exception { .... }

Есть ли лучший способ ждать ответного сообщения, кроме всего этого "синхронизированного (объекта) ожидания (..) уведомлять (..)" материал?

С уважением, bigbohne

Возможно, добавить spice:

Это должно закончиться веб-интерфейсом, где пользователь может запросить эти команды (либо через ajax, либо напрямую). Я также думал о кэшировании значений ответа в базе данных. Но для некоторых команд у вас MUSS есть прямой ответ на успех/неудачу

Ответ 1

Вы можете сделать это с помощью BlockingQueue, поэтому вам не придется вручную управлять какой-либо синхронизацией. Запрос может отправить сообщение, а затем вызвать take() в BlockingQueue, который будет ждать появления элемента. Элемент - это ответ, который вставляется в очередь любым слушателем, который у вас есть на порт при возврате ответа.

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

// Send method
BlockingQueue<Reply> q = new ArrayBlockingQueue<Reply>(1);
serialCom.registerQueue(myRequest.getId());
serialCom.sendRequest(myRequest);
return q.take();

//Listener
BlockingQueue<Reply> q = queueMap.get(incomingMessage.getId());
q.add(incomingMessage.getReply());

Ответ 2

Я не уверен, что я правильно понимаю вопрос, но обычно я использую Future<T> для этих видов задач.

Внутренне реализация Future<T> использует wait и notify и все это, но сам интерфейс становится довольно чистым.

Future<Double> getTemperature(int id);

В вашем коде связи вы можете отображать входящие сообщения в отношении невыполненных фьючерсов. Если заказ гарантирован, вы можете сделать что-то вроде

class Something {
    Map<Integer, Queue<Object>> requests;

    synchronized Future<?> request(int id, Object data) {
        MyFutureImpl future = new MyFuture();
        requests.get(id).add(future);
        serializeAndSend(id, data);
        return future;
    }

    void serializeAndSend(id, data) {...}

    synchronized void response(int id, Object data) {
        MyFutureImpl future = requests.get(id).remove();
        future.setValue(data); // This would fulfill the future, and any
                               // threads waiting in a get() will get the
                               // value and continue.
    }
}

Где MyFutureImpl - это очень простая реализация в будущем. Я предполагаю, что есть поток связи, который вызывает response() при получении пакета. Я также предполагаю, что serializeAndSend() -функция обрабатывает запись клиенту или блокирует до тех пор, пока операция записи не будет выполнена или не передана нишу связи.

Использование структур concurrency -capable map и queue может сделать некоторые synchronization ненужными. Если только один выдающийся вызов на один идентификатор, очередь становится ненужной, конечно.

Ответ 3

Лучший способ состоит в том, чтобы обернуть его в некоторый многоразовый класс запроса/ответа. Таким образом, вы можете иметь стандартный способ запроса последовательного порта и ждать ответа. Однако этот класс, несомненно, должен будет использовать синхронизированное ключевое слово и ждать и уведомлять команды где-то в своей реализации. Это ключевая часть написания Java, и важно понять, как они работают.

Если вы должны были создать такую ​​функцию, как вы говорите, вы должны убедиться, что вы вызываете ее из потока, который может блокировать ожидание результата. Функция должна будет выполнить какой-то цикл ожидания внутри него, ожидая ответа от последовательного порта. Поэтому важно, чтобы любой поток, на котором он работал, мог также ждать.

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

Здесь есть несколько сложных вопросов, и важно иметь хороший дизайн, который будет решать их все разумным образом.