Ответ №1:
Вы правы, что блокировка - это жизнеспособный подход, но есть гораздо более простой способ сделать все это. Boost имеет симпатичную небольшую конструкцию в ASIO, которая называется strand
. Любой обратный вызов, который был упакован с использованием цепочки, будет сериализован, гарантированно, независимо от того, какой поток выполняет обратный вызов. По сути, он обрабатывает любую блокировку для вас.
Это означает, что у вас может быть столько писателей, сколько вы хотите, и, если все они обернуты одной и той же цепью (так что поделитесь своей единственной цепочкой со всеми вашими писателями), они будут выполняться последовательно. Одна вещь, на которую следует обратить внимание, это убедиться, что вы не пытаетесь использовать один и тот же фактический буфер в памяти для выполнения всех операций записи. Например, вот чего следует избегать:
char buffer_to_write[256]; // shared among threads
/* ... in thread 1 ... */
memcpy(buffer_to_write, packet_1, std::min(sizeof(packet_1), sizeof(buffer_to_write)));
my_socket.async_write_some(boost::asio::buffer(buffer_to_write, sizeof(buffer_to_write)), &my_callback);
/* ... in thread 2 ... */
memcpy(buffer_to_write, packet_2, std::min(sizeof(packet_2), sizeof(buffer_to_write)));
my_socket.async_write_some(boost::asio::buffer(buffer_to_write, sizeof(buffer_to_write)), &my_callback);
Там вы делитесь своим реальным буфером записи (buffer_to_write
). Если вы сделали что-то подобное, вы будете в порядке:
/* A utility class that you can use */
class PacketWriter
{
private:
typedef std::vector<char> buffer_type;
static void WriteIsComplete(boost::shared_ptr<buffer_type> op_buffer, const boost::system::error_code& error, std::size_t bytes_transferred)
{
// Handle your write completion here
}
public:
template<class IO>
static bool WritePacket(const std::vector<char>& packet_data, IO& asio_object)
{
boost::shared_ptr<buffer_type> op_buffer(new buffer_type(packet_data));
if (!op_buffer)
{
return (false);
}
asio_object.async_write_some(boost::asio::buffer(*op_buffer), boost::bind(&PacketWriter::WriteIsComplete, op_buffer, boost::asio::placeholder::error, boost::asio::placeholder::bytes_transferred));
}
};
/* ... in thread 1 ... */
PacketWriter::WritePacket(packet_1, my_socket);
/* ... in thread 2 ... */
PacketWriter::WritePacket(packet_2, my_socket);
Здесь будет полезно, если вы также передадите свою ветвь в WritePacket. Вы поняли, хотя.
Ответ № 2:
Я думаю, вы уже используете очень хороший подход. Одно из предложений, которое я бы предложил, - это использовать async_write
вместо async_write_some
, чтобы гарантировать, что весь буфер будет записан до вызова вашего обратного вызова.