одновременный async_write.есть ли решение без ожидания? - PullRequest
3 голосов
/ 07 мая 2011

async_write() запрещено вызывать одновременно из разных потоков.Он отправляет данные порциями, используя async_write_some, и такие порции могут чередоваться.Таким образом, пользователь должен позаботиться о том, чтобы не вызывать async_write() одновременно.

Есть ли более приятное решение, чем этот псевдокод?

void send(shared_ptr<char> p) {
  boost::mutex::scoped_lock lock(m_write_mutex);
  async_write(p, handler);
}

Мне не нравится идея заблокироватьдругие потоки в течение довольно длительного времени (в моем приложении около 50 Мб).

Может быть, что-то подобное будет работать?

void handler(const boost::system::error_code& e) {
  if(!e) {
    bool empty = lockfree_pop_front(m_queue);
    if(!empty) {
      shared_ptr<char> p = lockfree_queue_get_first(m_queue);
      async_write(p, handler);
    }
  }
}

void send(shared_ptr<char> p) {
  bool q_was_empty = lockfree_queue_push_back(m_queue, p)
  if(q_was_empty)
    async_write(p, handler);
}

Я бы предпочел найти готовыйиспользовать рецепт поваренной книги.Работать с free-free нелегко, может появиться множество мелких ошибок.

Ответы [ 2 ]

4 голосов
/ 09 мая 2011

async_write () запрещено вызывать одновременно из разных потоков

Это утверждение не совсем корректно.Приложения могут свободно вызывать async_write одновременно, если они находятся на разных socket объектах.

Есть ли более приятное решение, чем этот псевдокод?

void send(shared_ptr<char> p) {
  boost::mutex::scoped_lock lock(m_write_mutex);
  async_write(p, handler);
}

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

Существуют более удачные решения этой проблемы:Библиотека имеет встроенную поддержку, используя концепцию strand .Это хорошо вписывается в этот сценарий.

Нить определяется как строго последовательный вызов обработчиков событий (т.е. без одновременного вызова).Использование цепочек позволяет выполнять код в многопоточной программе без необходимости явной блокировки (например, с использованием мьютексов).

Использование здесь явной цепочки гарантирует, что ваши обработчики будут вызываться только одним потоком, который имеетвызвано io_service::run().В вашем примере элемент m_queue будет защищен цепью, обеспечивающей атомарный доступ к очереди исходящих сообщений.После добавления записи в очередь, если размер равен 1, это означает, что не выполняется ни одной не выполненной операции async_write, и приложение может инициировать ее, обернутую через цепочку.Если размер очереди больше 1, приложение должно дождаться завершения async_write.В обработчике завершения async_write откройте запись из очереди и при необходимости обработайте все ошибки.Если очередь не пуста, обработчик завершения должен инициировать еще один async_write с начала очереди.

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

1 голос
/ 19 мая 2011

Мы решили эту проблему, поместив отдельную очередь данных для записи в наш объект сокета.Когда первая часть данных для записи «в очереди», мы начинаем async_write().В нашем обработчике завершения async_write мы запускаем последующие операции async_write, если еще есть данные для передачи.

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