Странное поведение io_context или std :: cout в многопоточном UDP-сервере - PullRequest
1 голос
/ 25 февраля 2020

У меня есть простой UDP-сервер, созданный с использованием boost\asio, который пока только печатает полученные данные, как показано ниже.

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;
    }
}

Я создаю 3 потока функции server, используя boost::thread, который назначаю 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();

Для проверки сервера, который я использовал Отправитель пакета . Проблема в том, что вывод ... scrambled . При отправке пакетов на 3 порта (более или менее) в одно и то же время один раз в секунду выходной сигнал слегка смещается, но при увеличении частоты пакетов до одного раза в 0,1 секунды выходной сигнал становится нечитаемым, как показано в , эти два изображения . Я попытался дать один отдельный объект io_context каждому серверу, но на высокой частоте выходной сигнал остается прежним. Есть ли способ избежать этого?

Ответы [ 2 ]

3 голосов
/ 25 февраля 2020

Логичным (и правильным) решением является использование взаимного исключения на std::cout. Вы знаете соответствующую область действия блокировки (в вашем случае это всего лишь один пакет UDP, но std::cout не может догадаться об этом).

Необычное решение: boost.asio.strand. Вам не нужно это для простых случаев, подобных этому, но поскольку вы пытаетесь использовать boost.asio.io_context, вы должны знать, что в boost.asio есть еще один класс, который мог бы работать так, как вы предполагали.

1 голос
/ 25 февраля 2020

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;
...