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.как они предназначены.Этот другой ответ, который я написал содержит некоторый код, реализующий этот дизайн.