如何将数据写入标准输入以供等待标准输入输入的单独线程使用?
How to write data to stdin to be consumed by a separate thread waiting on input from stdin?
我正在尝试在与主线程不同的线程中从 stdin 读取一些数据。主线程应该能够通过写入 stdin 来与这个等待线程通信,但是当我 运行 测试代码(包含在下面)时,除了打印消息(我的测试代码中的 'do_some_work' )之外没有任何反应直接在终端上而不是从等待线程输出。
我尝试了 SO 上列出的几个解决方案,但没有成功。我的代码模仿了以下 SO 问题的解决方案之一,它本身工作得很好,但是当与我的 read_stdin_thread 结合使用时,它却没有。
Is it possible to write data into own stdin in 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;
}
说明如何写入 stdin 并由 threadReadStdin 读取的代码片段将非常有用。
在此先致谢!
编辑:
有一件事我在这里忘了提,readStdin() 中的代码是第三方代码,发生的任何类型的通信都必须遵守其条款。
此外,我可以很容易地将 std::cin 和 std::cout 重定向到 fstream 或 stringstream。问题是,当我写入重定向的 cin 缓冲区时,读取线程上什么也没有真正出现。
编辑2:
这是一个单进程应用程序,生成不是一个选项。
如果你想在同一个程序的不同线程之间使用管道进行通信,你不应该尝试使用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];
}
}
正在写消息
接下来,我们定义一个函数来写入消息。该函数以 lambda 形式给出,因此我们可以将其直接传递给线程。
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);
};
正在阅读消息
我们还应该做一个阅读消息的功能。阅读消息将在不同的线程上完成。此 lambda 以某种类型读取 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);
};
主要方法
终于可以写main方法了。它打开通道,在一个线程上写入消息,并在另一个线程上读取它。
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();
}
在@Jorge Perez、@Remy Lebeau 和@Kamil Cuk 非常有建设性的回复的帮助下,我似乎偶然发现了答案。该解决方案建立在@Jorge Perez 非常有用的代码之上。为了简洁起见,我没有包括整个代码,但部分来自我发布的代码,很大一部分来自@Jorge Perez 的代码。
我所做的是采用他使用管道的方法,并使用 dup 将 STDIN_FILENO 替换为管道读取 fd。关注 link 真的很有帮助:
https://en.wikipedia.org/wiki/Dup_(system_call)
考虑到我在生产环境代码中的限制,我非常感谢您就这是否是 hack 或足够好提出意见 approach/solution。
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;
}
我正在尝试在与主线程不同的线程中从 stdin 读取一些数据。主线程应该能够通过写入 stdin 来与这个等待线程通信,但是当我 运行 测试代码(包含在下面)时,除了打印消息(我的测试代码中的 'do_some_work' )之外没有任何反应直接在终端上而不是从等待线程输出。
我尝试了 SO 上列出的几个解决方案,但没有成功。我的代码模仿了以下 SO 问题的解决方案之一,它本身工作得很好,但是当与我的 read_stdin_thread 结合使用时,它却没有。
Is it possible to write data into own stdin in 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;
}
说明如何写入 stdin 并由 threadReadStdin 读取的代码片段将非常有用。
在此先致谢!
编辑:
有一件事我在这里忘了提,readStdin() 中的代码是第三方代码,发生的任何类型的通信都必须遵守其条款。
此外,我可以很容易地将 std::cin 和 std::cout 重定向到 fstream 或 stringstream。问题是,当我写入重定向的 cin 缓冲区时,读取线程上什么也没有真正出现。
编辑2:
这是一个单进程应用程序,生成不是一个选项。
如果你想在同一个程序的不同线程之间使用管道进行通信,你不应该尝试使用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];
}
}
正在写消息
接下来,我们定义一个函数来写入消息。该函数以 lambda 形式给出,因此我们可以将其直接传递给线程。
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);
};
正在阅读消息
我们还应该做一个阅读消息的功能。阅读消息将在不同的线程上完成。此 lambda 以某种类型读取 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);
};
主要方法
终于可以写main方法了。它打开通道,在一个线程上写入消息,并在另一个线程上读取它。
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();
}
在@Jorge Perez、@Remy Lebeau 和@Kamil Cuk 非常有建设性的回复的帮助下,我似乎偶然发现了答案。该解决方案建立在@Jorge Perez 非常有用的代码之上。为了简洁起见,我没有包括整个代码,但部分来自我发布的代码,很大一部分来自@Jorge Perez 的代码。
我所做的是采用他使用管道的方法,并使用 dup 将 STDIN_FILENO 替换为管道读取 fd。关注 link 真的很有帮助:
https://en.wikipedia.org/wiki/Dup_(system_call)
考虑到我在生产环境代码中的限制,我非常感谢您就这是否是 hack 或足够好提出意见 approach/solution。
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;
}