C++ 并行程序中的I/O

C++ 并行程序中的I/O,c++,concurrency,io,C++,Concurrency,Io,我在做一个并行程序;它有两个线程,一个侦听来自服务器的消息,另一个向服务器发送消息。 我需要从用户那里获取命令(使用cin?)并同时显示来自服务器的消息 我如何处理这种情况?问题是,如果我在收到消息时从用户那里读取命令,那么用户的输入就会与其他内容混淆 提前感谢一些选择 让您的命令转储自上次调用该命令以来发生的所有消息。这样,输出是有限的 让您的cli命令连续监视所有通信量,直到按下ctrl-c(或其他一些组合键),然后返回到应用程序的cli提示符 让cli命令将数据发送到文件,并使用tailt

我在做一个并行程序;它有两个线程,一个侦听来自服务器的消息,另一个向服务器发送消息。 我需要从用户那里获取命令(使用cin?)并同时显示来自服务器的消息

我如何处理这种情况?问题是,如果我在收到消息时从用户那里读取命令,那么用户的输入就会与其他内容混淆

提前感谢

一些选择

  • 让您的命令转储自上次调用该命令以来发生的所有消息。这样,输出是有限的

  • 让您的cli命令连续监视所有通信量,直到按下ctrl-c(或其他一些组合键),然后返回到应用程序的cli提示符

  • 让cli命令将数据发送到文件,并使用
    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”
    ),输出内容将被打印,当前输入缓冲区将再次回显。这是示例代码的一部分。这就是为什么它更希望保持短,而不是完美或最用户舒适。。。