多线程 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;