C++ 并行程序中的I/O
我在做一个并行程序;它有两个线程,一个侦听来自服务器的消息,另一个向服务器发送消息。 我需要从用户那里获取命令(使用cin?)并同时显示来自服务器的消息 我如何处理这种情况?问题是,如果我在收到消息时从用户那里读取命令,那么用户的输入就会与其他内容混淆 提前感谢一些选择C++ 并行程序中的I/O,c++,concurrency,io,C++,Concurrency,Io,我在做一个并行程序;它有两个线程,一个侦听来自服务器的消息,另一个向服务器发送消息。 我需要从用户那里获取命令(使用cin?)并同时显示来自服务器的消息 我如何处理这种情况?问题是,如果我在收到消息时从用户那里读取命令,那么用户的输入就会与其他内容混淆 提前感谢一些选择 让您的命令转储自上次调用该命令以来发生的所有消息。这样,输出是有限的 让您的cli命令连续监视所有通信量,直到按下ctrl-c(或其他一些组合键),然后返回到应用程序的cli提示符 让cli命令将数据发送到文件,并使用tailt
tail
type工具监视该文件我使用了我的旧示例代码,并试图将其转换为。(“最小”不一定意味着“短”,是吗?) 这是一个非常简单的“shell”概念,它支持一个线程进行输入,而多个线程可以进行输出
getChar()
——一种用于MS Windows,另一种用于非MS Windows(实际上只考虑*ix操作系统)。后者“深受启发”std::string
中“\b\b”
输出),打印输出文本(包括换行符),然后再次打印提示和当前输入缓冲区miniShell.cc
:
// system header:
#ifdef _WIN32
#include <conio.h>
#else // (not) _WIN32
#include <termios.h>
#include <unistd.h>
#include <stdio.h>
#endif // _WIN32
/// reads a character from console without echo.
#ifdef _WIN32
inline int getChar() { return _getch(); }
#else // (not) _WIN32
int getChar()
{
struct termios oldattr;
tcgetattr(STDIN_FILENO, &oldattr);
struct termios newattr = oldattr;
newattr.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &newattr);
const int ch = getchar();
tcsetattr(STDIN_FILENO, TCSANOW, &oldattr);
return ch;
}
#endif // _WIN32
// standard C/C++ header:
#include <cstring>
#include <mutex>
#include <string>
/* provides a class for a simple thread-safe mini-shell.
*
* It is assumed that one thread may await user input (read()) while
* another thread may (or may not) output text from time to time.
* The mini-shell grants that the input line is always the last line.
*/
class Console {
// variable:
private:
// mutex for console I/O
std::mutex _mtx;
// current input
std::string _input;
// prompt output
std::string _prompt;
// methods:
public:
/// constructor.
Console() { }
// disabled:
Console(const Console&) = delete;
Console& operator = (const Console&) = delete;
// reads a line from console and returns input string
std::string read();
/* writes text to console.
*
* text the text
* size size of text
*/
void write(const char *text, size_t size);
void write(const char *text) { write(text, strlen(text)); }
void write(const std::string &text) { write(text.c_str(), text.size()); }
};
// standard C/C++ header:
#include <atomic>
#include <chrono>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <thread>
std::string Console::read()
{
{ // activate prompt
std::lock_guard<std::mutex> lock(_mtx);
_prompt = "> "; _input.clear();
std::cout << _prompt << std::flush;
}
#ifdef _WIN32
enum { Enter = '\r', BackSpc = '\b' };
#else // (not) _WIN32
enum { Enter = '\n', BackSpc = 127 };
#endif // _WIN32
// input loop
for (;;) {
switch (int c = getChar()) {
case Enter: {
std::lock_guard<std::mutex> lock(_mtx);
std::string input = _input;
_prompt.clear(); _input.clear();
std::cout << std::endl;
return input;
} // unreachable: break;
case BackSpc: {
std::lock_guard<std::mutex> lock(_mtx);
if (_input.empty()) break; // nothing to do
_input.pop_back();
std::cout << "\b \b" << std::flush;
} break;
default: {
if (c < ' ' || c >= '\x7f') break;
std::lock_guard<std::mutex> lock(_mtx);
_input += c;
std::cout << (char)c << std::flush;
} break;
}
}
}
void Console::write(const char *text, size_t len)
{
if (!len) return; // nothing to do
bool eol = text[len - 1] == '\n';
std::lock_guard<std::mutex> lock(_mtx);
// remove current input echo
if (size_t size = _prompt.size() + _input.size()) {
std::cout
<< std::setfill('\b') << std::setw(size) << ""
<< std::setfill(' ') << std::setw(size) << ""
<< std::setfill('\b') << std::setw(size) << "";
}
// print text
std::cout << text;
if (!eol) std::cout << std::endl;
// print current input echo
std::cout << _prompt << _input << std::flush;
}
// a sample application
// shared data for main thread and data processing thread
struct Shared {
// flag: true ... exit communication thread and main loop
std::atomic<bool> exit;
// flag: true ... start data processing
std::atomic<bool> start;
// the mini console
Console console;
// constructor.
Shared(): exit(false), start(true) { }
};
void dataProc(Shared &shared)
{
while (!shared.exit) {
// "busy" wait for start (condition would be more elegant)
while (!shared.start) {
if (shared.exit) return;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
// do data processing
shared.console.write("Starting data processing.");
for (int i = 0, n = 20; i < n; ++i) {
// "busy" wait for start (condition would be more elegant)
if (!shared.start) {
shared.console.write("Data processing stopped.");
while (!shared.start) {
if (shared.exit) return;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
shared.console.write("Data processing restarted.");
}
// consume some time (to simulate heavy computation)
std::this_thread::sleep_for(std::chrono::milliseconds(250));
// do some console output about progress
{ std::ostringstream fmt;
fmt << "Step " << i + 1 << '/' << n;
shared.console.write(fmt.str());
}
}
shared.console.write("Data processing done.");
shared.start = false;
}
}
void processInput(const std::string &input, Shared &shared)
{
if (input == "start") shared.start = true;
else if (input == "stop") shared.start = false;
else if (input == "exit") shared.exit = true;
else if (input.size()) shared.console.write("Wrong command!");
}
int main()
{
Shared shared;
// start a thread for some kind of data processing
std::thread threadDataProc(&dataProc, std::ref(shared));
// main loop
while (!shared.exit) {
shared.console.write("Commands: start stop exit");
std::string input = shared.console.read();
processInput(input, shared);
}
// join data processing thread
threadDataProc.join();
// done
return 0;
}
//系统头:
#ifdef_WIN32
#包括
#else/(not)\u WIN32
#包括
#包括
#包括
#endif/\u WIN32
///从控制台读取字符而不进行回显。
#ifdef_WIN32
内联int getChar(){return _getch();}
#else/(not)\u WIN32
int getChar()
{
结构termios oldattr;
tcgetattr(标准文件号和旧属性);
struct termios newattr=oldattr;
newattr.c|lflag&=~(ICANON | ECHO);
tcsetattr(标准文件号、TCSANOW和newattr);
const int ch=getchar();
tcsetattr(标准文件号、TCSANOW和旧属性);
返回ch;
}
#endif/\u WIN32
//标准C/C++头文件:
#包括
#包括
#包括
/*为简单的线程安全迷你shell提供一个类。
*
*假设一个线程在运行时可能会等待用户输入(read())
*另一个线程可能会(也可能不会)不时输出文本。
*迷你shell允许输入行始终是最后一行。
*/
类控制台{
//变量:
私人:
//控制台I/O的互斥锁
std::mutex\u mtx;
//电流输入
std::string\u输入;
//提示输出
std::string\u提示符;
//方法:
公众:
///构造器。
控制台(){}
//残疾人士:
控制台(const Console&)=删除;
控制台和操作员=(const Console&)=删除;
//从控制台读取一行并返回输入字符串
std::string read();
/*将文本写入控制台。
*
*文本文本
*文本大小
*/
无效写入(常量字符*文本,大小);
void write(const char*text){write(text,strlen(text));}
void write(const std::string&text){write(text.c_str(),text.size());}
};
//标准C/C++头文件:
#包括
请记住,我写了这段代码,简单比完美或舒适更重要 仅使用cin
和cout
很难将输入和输出与普通命令行窗口或终端进行混合。如果你远离C++标准的输入和输出流,同时仍然保持文本模式,那么你就有了类似的东西。否则,您可以执行一个非常简单的GUI前端程序,执行主程序,将输入写入主程序的标准输入,并读取其标准输出以显示。在Unix/Linux上使用GUI的解决方案可以是:创建一个新的终端(窗口或拆分现有终端的视图)然后将输出重定向到另一个终端。我曾经用一种非常简单的方法做过:用一个不回显的getchar函数读取键输入。(这不是可移植的,但我找到了适用于Windows和Linux的解决方案,目前已经足够了。)当前的输入被缓冲在std::string
中,并由我自己的代码进行响应。每当输出完成时,当前回显的输入将被擦除(cout
ing适当数量的“\b\b”
),输出内容将被打印,当前输入缓冲区将再次回显。这是示例代码的一部分。这就是为什么它更希望保持短,而不是完美或最用户舒适。。。