Лучший способ синхронизировать асинхронную задачу - PullRequest
12 голосов
/ 04 октября 2010

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

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

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

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

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

Может быть, добавить немного специй:

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

Ответы [ 3 ]

7 голосов
/ 04 октября 2010

Вы можете сделать это с 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 голосов
/ 04 октября 2010

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

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

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() -функция обрабатывает запись клиенту или блокам, пока операция записи не может быть выполнена или передана потоку связи.

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

1 голос
/ 04 октября 2010

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...