Как записать данные в stdin для использования отдельным потоком, ожидающим ввода от stdin? - PullRequest
0 голосов
/ 10 апреля 2019

Я пытаюсь прочитать некоторые данные из stdin в отдельном потоке из основного потока.Основной поток должен иметь возможность общаться с этим ожидающим потоком, записывая в stdin, но когда я запускаю тестовый код (включенный ниже), ничего не происходит, за исключением того, что сообщение ('do_some_work' в моем тестовом коде) печатается непосредственно на терминале вместовывод из ожидающего потока.

Я пробовал пару решений, перечисленных в SO, но безуспешно.Мой код имитирует одно из решений из следующего SO вопроса, и он прекрасно работает сам по себе, но в сочетании с моим read_stdin_thread это не так.

Возможно ли записать данные в собственный stdin в Linux

#include <unistd.h>
#include <string>
#include <iostream>
#include <sstream>
#include <thread>

bool terminate_read = true;

void readStdin() {

    static const int INPUT_BUF_SIZE = 1024;
    char buf[INPUT_BUF_SIZE];

    while (terminate_read) {
        fd_set readfds;
        struct timeval tv;
        int data;

        FD_ZERO(&readfds);
        FD_SET(STDIN_FILENO, &readfds);
        tv.tv_sec=2;
        tv.tv_usec=0;
        int ret = select(16, &readfds, 0, 0, &tv);
        if (ret == 0) {
            continue;
        } else if (ret == -1) {
            perror("select");
            continue;
        }
        data=FD_ISSET(STDIN_FILENO, &readfds);
        if (data>0) {
            int bytes = read(STDIN_FILENO,buf,INPUT_BUF_SIZE);
            if (bytes == -1) {
                perror("input poll: read");
                continue;
            }
            if (bytes) {
                std::cout << "Execute: " << buf << std::endl;
                if (strncmp(buf, "quit", 4)==0) {
                    std::cout << "quitting reading from stdin." << std::endl;
                    break;
                }
                else {
                    continue;
                }
            }
        }
    }
}

int main() {
    std::thread threadReadStdin([] () {
        readStdin();
    });

    usleep(1000000);
    std::stringstream msg;
    msg << "do_some_work" << std::endl;
    auto s = msg.str();
    write(STDIN_FILENO, s.c_str(), s.size());
    usleep(1000000);

    terminate_read = false;
    threadReadStdin.join();

    return 0;
}

Фрагмент кода, иллюстрирующий, как писать на стандартный ввод, который, в свою очередь, читается threadReadStdin, был бы чрезвычайно полезен.

Заранее большое спасибо!

Редактировать:

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

Также ядовольно легко перенаправить std :: cin и std :: cout в fstream или stringstream.Проблема в том, что когда я пишу в перенаправленный буфер cin, в потоке чтения на самом деле ничего не появляется.

Edit2:

Это приложение с одним процессом, и порождение не является опцией.

Ответы [ 2 ]

1 голос
/ 10 апреля 2019

Если вы хотите использовать канал для связи между различными потоками в одной и той же программе, вы не должны пытаться использовать stdin или stdout. Вместо этого просто используйте функцию pipe, чтобы создать свой собственный канал. Я проведу вас через это шаг за шагом!

Открытие канала

Давайте создадим вспомогательную функцию для открытия канала, используя pipe. Эта функция принимает два целых числа по ссылке - конец чтения и конец записи. Он пытается открыть канал и, если не может, выдает ошибку.

#include <unistd.h>
#include <cstdio>
#include <thread>
#include <string>

void open_channel(int& read_fd, int& write_fd) {
    int vals[2];
    int errc = pipe(vals); 
    if(errc) {
        fputs("Bad pipe", stderr); 
        read_fd = -1;
        write_fd = -1; 
    } else {
        read_fd = vals[0];
        write_fd = vals[1]; 
    }
}

Написание сообщения

Далее мы определяем функцию для написания сообщения. Эта функция задана как лямбда, так что мы можем передать ее непосредственно потоку.

auto write_message = [](int write_fd, std::string message) {
    ssize_t amnt_written = write(write_fd, message.data(), message.size());
    if(amnt_written != message.size()) {
        fputs("Bad write", stderr); 
    }
    close(write_fd); 
}; 

Чтение сообщения

Мы также должны создать функцию для чтения сообщения. Чтение сообщения будет сделано в другой ветке. Эта лямбда читает сообщение 1000 байтов в типе и выводит его на стандартный вывод.

auto read_message = [](int read_fd) {
    constexpr int buffer_size = 1000; 
    char buffer[buffer_size + 1]; 
    ssize_t amnt_read; 
    do {
        amnt_read = read(read_fd, &buffer[0], buffer_size);
        buffer[amnt_read] = 0; 
        fwrite(buffer, 1, amnt_read, stdout); 
    } while(amnt_read > 0); 
};

Основной метод

Наконец, мы можем написать основной метод. Он открывает канал, записывает сообщение в один поток и читает его в другой поток.

int main() {
    int read_fd;
    int write_fd;
    open_channel(read_fd, write_fd); 

    std::thread write_thread(
        write_message, write_fd, "Hello, world!"); 
    std::thread read_thread(
        read_message, read_fd); 
    write_thread.join(); 
    read_thread.join(); 
}
0 голосов
/ 10 апреля 2019

Похоже, что я наткнулся на ответ с помощью очень конструктивных ответов @Jorge Perez, @Remy Lebeau и @Kamil Cuk. Это решение основано на чрезвычайно полезном коде @Jorge Perez. Ради краткости я не включаю весь код, но часть взята из кода, который я разместил, а большая часть взята из кода @Jorge Perez.

Что я сделал, так это использовал его подход с использованием каналов и заменой STDIN_FILENO на канал, считывающий fd с использованием dup. Следующая ссылка была действительно полезна:

https://en.wikipedia.org/wiki/Dup_(system_call)

Буду очень признателен за ваш вклад в то, является ли это хаком или достаточно хорошим подходом / решением, учитывая ограничения, которые у меня есть в коде производственной среды.

int main() {
    int read_fd;
    int write_fd;

    open_channel(read_fd, write_fd); 

    close(STDIN_FILENO);
    if(dup(read_fd) == -1)
        return -1;

    std::thread write_thread(write_message, write_fd, "Whatsup?"); 
    std::thread threadReadStdin([] () {
        readStdin();
    });

    write_thread.join(); 
    threadReadStdin.join();

    return 0;
}
...