Прервать цикл getLine() while в C++

Я сделал консольное приложение, которое принимает команды из двух источников:

  1. Фактическая консоль -> это цикл while(getLine()) в отдельном потоке.
  2. Сервер веб-сокета -> это также работает в отдельном потоке

Если есть введенная команда, команда сохраняется в векторе до тех пор, пока другой цикл while (который выполняется каждые 20 мс) не будет проходить через все команды, введенные за прошедшее время. Если он читает команду, он ее выполняет.

Теперь есть команда Stop, которая останавливает приложение. При входе приложение закрывается, как и ожидалось. Но проблема в том, что это занимает некоторое время, и вы все еще можете вводить текст из первого источника команды (getline()). Как только вы что-то набираете, последовательность выключения останавливается и ждет, пока вы не нажмете ввод.

Я завершаю первый поток (который содержит цикл getline) после запуска последовательности выключения. Но это не работает...

Любые идеи?

Заранее спасибо!


person Antony    schedule 23.11.2014    source источник
comment
Я не могу этого сделать, потому что тот тред не знает, и когда он получит это сообщение, будет уже слишком поздно...   -  person Antony    schedule 23.11.2014
comment
Я не могу загрузить код, существующий из 50 классов и заголовков. Я постарался объяснить структуру как можно лучше. Мой код не нужен, мне просто нужен способ отменить действие getline(), которое зацикливается через какое-то время. Если вы действительно считаете, что код нужен, я постараюсь опубликовать соответствующие фрагменты.   -  person Antony    schedule 23.11.2014


Ответы (2)


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

На этот вопрос, кажется, есть несколько соответствующих ответов: Peek stdin с использованием pthreads

Кстати, вы упомянули вектор, доступ к которому (если я правильно понял) осуществляется из нескольких потоков. Вам придется позаботиться о правильной синхронизации (например, об использовании мьютекса при доступе к вектору).

Кроме того, тот факт, что у вас есть какой-то цикл, который "опрашивает" вектор каждые 20 мс, указывает на то, что у вас могут быть некоторые недостатки в общем дизайне вашего приложения. Попробуйте избавиться от него, используя более подходящие средства для передачи событий между потоками, такие как условные переменные.

person Mikhail Maltsev    schedule 23.11.2014
comment
Я использую стандартные библиотеки C++ для потоков. Чтобы добавить строку в вектор, я использую мьютекс, так что проблем нет. Для цикла, который запускается каждые 20 мс: я не вижу альтернативы, потому что будет больше потоков, которые будут отправлять команды. И что вы имеете в виду под «использовать что-то другое». Я посмотрел через cin ››, но он делает то же самое: ждет ввода. - person Antony; 23.11.2014

Проблема в том, что getline является блокирующим вызовом и вернется сразу после того, как вы нажмете ENTER в случае стандартного ввода.

У меня была аналогичная проблема, и я решил ее, как показано ниже. Я использовал два файловых дескриптора: один для мониторинга стандартного ввода, а другой для мониторинга «самостоятельной трубы». Последний действует как триггер для разблокировки select в случае возникновения какого-либо события. Первый гарантирует, что getline вызывается только после того, как он сможет прочитать полную строку.

#include <future>
#include <string>
#include <iostream>
#include <thread>
#include <unistd.h>
#include <stdio.h>
#include <sys/select.h>

int pipe_fd[2];
auto p = pipe(pipe_fd);
auto stdin_fd = fileno(stdin); // 0

fd_set check_fd;


int main(int argc, char const *argv[])
{
    FD_ZERO(&check_fd);
    FD_SET(stdin_fd, &check_fd);
    FD_SET(pipe_fd[0], &check_fd);

    auto t1 = std::async(std::launch::async, [] {
        std::this_thread::sleep_for(std::chrono::seconds(10));
        uint32_t dummy = 43;
        // this will stop the data input
        write(pipe_fd[1], &dummy, sizeof(dummy));
    });

    auto maxfd = pipe_fd[0] > pipe_fd[1] ? pipe_fd[0] : pipe_fd[1];
    select(maxfd + 1, &check_fd, nullptr, nullptr, nullptr);
    if (FD_ISSET(stdin_fd, &check_fd))
    {
        std::string input;
        std::getline(std::cin, input);
        std::cout << "You enterd:" << input << std::endl;
    }

    if (FD_ISSET(pipe_fd[0], &check_fd))
        std::cout << "Event" << std::endl;

    return 0;
}

person Sascha P    schedule 05.06.2020