多线程 UDP 服务器中 io_context 或 std::cout 的奇怪行为
Strange behaviour of either io_context or std::cout in multi-threaded UDP server
我有一个使用 boost\asio
创建的简单 UDP 服务器,目前它只打印接收到的数据,如下所示。
using boost::asio::ip::udp;
enum { max_length = 2048 };
void server(boost::asio::io_context& io_context, unsigned short port)
{
std::cout << "Server Created on port " + std::to_string(port) << " \n" << std::flush;
udp::socket sock(io_context, udp::endpoint(udp::v4(), port));
while(true)
{
udp::endpoint sender_endpoint;
std::vector<char> v(max_length);
sock.receive_from(boost::asio::buffer(v), sender_endpoint);
for (int i = 0; i < v.size(); i++)
{
std::cout << v[i] <<std::flush;
}
std::cout << std::endl;
}
}
我使用 boost::thread
创建了 server
函数的 3 个线程,我将其分配给 thread_group
:
boost::asio::io_context io_context;
boost::thread_group tg;
boost::thread *t = new boost::thread(server, std::ref(io_context), 8889);
boost::thread *t1 = new boost::thread(server, std::ref(io_context), 8890);
boost::thread *t2 = new boost::thread(server, std::ref(io_context), 11111);
tg.add_thread(t);
tg.add_thread(t1);
tg.add_thread(t2);
tg.join_all();
为了测试我使用的服务器Packet Sender。问题是输出是……乱码。
当每秒(或多或少)同时在 3 个端口上发送数据包时,输出会稍微错位,但是当将数据包频率增加到每 0.1 秒一次时,输出变得不可读,如 these two images 所示.我曾尝试为每台服务器提供一个单独的 io_context
对象,但输出在高频率下保持不变。有什么办法可以避免这种情况吗?
合乎逻辑(且正确)的解决方案是在 std::cout
上使用互斥。您知道锁定的适当范围(在您的情况下,只是一个 UDP 数据包,但 std::cout
无法猜测)。
奇特的解决方案是 boost.asio.strand
。对于像这样的简单情况,您不需要它,但由于您正在尝试使用 boost.asio.io_context
,您应该知道 boost.asio
中还有另一个 class 可以像您预期的那样工作。
std::cout
自动为您做一些锁定,单个打印操作不会与来自不同线程的另一个打印重叠。但是,当您一次打印一个字符时,每个线程的输出可能会重叠。在每个打印字符后刷新也可能导致性能不佳。
如果将要打印的内容构建到单个字符串中,它应该可以正确打印:
std::vector<char> v(max_length);
size_t bytes = sock.receive_from(boost::asio::buffer(v), sender_endpoint);
std::string str(v.data(), bytes);
str += '\n';
std::cout << str;
或者您可以跳过矢量并直接保存为字符串:
std:: string str(max_length);
size_t bytes = sock.receive_from(boost::asio::buffer(str), sender_endpoint);
str.resize(bytes)
str += '\n';
std::cout << str;
我有一个使用 boost\asio
创建的简单 UDP 服务器,目前它只打印接收到的数据,如下所示。
using boost::asio::ip::udp;
enum { max_length = 2048 };
void server(boost::asio::io_context& io_context, unsigned short port)
{
std::cout << "Server Created on port " + std::to_string(port) << " \n" << std::flush;
udp::socket sock(io_context, udp::endpoint(udp::v4(), port));
while(true)
{
udp::endpoint sender_endpoint;
std::vector<char> v(max_length);
sock.receive_from(boost::asio::buffer(v), sender_endpoint);
for (int i = 0; i < v.size(); i++)
{
std::cout << v[i] <<std::flush;
}
std::cout << std::endl;
}
}
我使用 boost::thread
创建了 server
函数的 3 个线程,我将其分配给 thread_group
:
boost::asio::io_context io_context;
boost::thread_group tg;
boost::thread *t = new boost::thread(server, std::ref(io_context), 8889);
boost::thread *t1 = new boost::thread(server, std::ref(io_context), 8890);
boost::thread *t2 = new boost::thread(server, std::ref(io_context), 11111);
tg.add_thread(t);
tg.add_thread(t1);
tg.add_thread(t2);
tg.join_all();
为了测试我使用的服务器Packet Sender。问题是输出是……乱码。
当每秒(或多或少)同时在 3 个端口上发送数据包时,输出会稍微错位,但是当将数据包频率增加到每 0.1 秒一次时,输出变得不可读,如 these two images 所示.我曾尝试为每台服务器提供一个单独的 io_context
对象,但输出在高频率下保持不变。有什么办法可以避免这种情况吗?
合乎逻辑(且正确)的解决方案是在 std::cout
上使用互斥。您知道锁定的适当范围(在您的情况下,只是一个 UDP 数据包,但 std::cout
无法猜测)。
奇特的解决方案是 boost.asio.strand
。对于像这样的简单情况,您不需要它,但由于您正在尝试使用 boost.asio.io_context
,您应该知道 boost.asio
中还有另一个 class 可以像您预期的那样工作。
std::cout
自动为您做一些锁定,单个打印操作不会与来自不同线程的另一个打印重叠。但是,当您一次打印一个字符时,每个线程的输出可能会重叠。在每个打印字符后刷新也可能导致性能不佳。
如果将要打印的内容构建到单个字符串中,它应该可以正确打印:
std::vector<char> v(max_length);
size_t bytes = sock.receive_from(boost::asio::buffer(v), sender_endpoint);
std::string str(v.data(), bytes);
str += '\n';
std::cout << str;
或者您可以跳过矢量并直接保存为字符串:
std:: string str(max_length);
size_t bytes = sock.receive_from(boost::asio::buffer(str), sender_endpoint);
str.resize(bytes)
str += '\n';
std::cout << str;