2011-05-29 819 views
23

我正在尋找一種(多平臺)方式爲我的C++程序執行非阻塞控制檯輸入,因此我可以在程序不斷運行時處理用戶命令。該計劃也將同時輸出信息。非阻塞控制檯輸入C++

什麼是最好的/最簡單的方法來做到這一點?使用外部庫如boost,只要他們使用許可許可證,就沒有問題。

+2

可以爲你一個普通的線程庫的工作? – Steinbitglis 2011-05-29 23:40:56

+0

@Steinbitglis:什麼是「普通」線程庫,它與其他任何線程庫有什麼不同? – 2011-05-29 23:46:18

+1

@Tomalak我認爲他的意思是一個線程庫,一個非阻塞的IO庫。 – 2011-05-29 23:47:26

回答

8

我會通過創建一個獨立的線程來調用普通的阻塞IO函數,並將它傳遞給一個回調函數,它會在輸入時調用它。你確定你需要做你說的你想做的事嗎?

至於在同一時間輸出信息,如果用戶正在鍵入某些輸入和打印某些內容時會發生什麼?

+1

如果我有一個線程等待說... cin處理輸入,然後另一個線程會發生什麼線程使用cout輸出控制檯?那會很糟嗎? – Doug 2011-05-29 23:53:59

+2

@Doug你不應該真的在輸出和從控制檯輸入/從不同的線程(除非你想有一個同步對象來保持它們一致,這可能會使你首先使用線程的原因順其自然)。我以前沒有嘗試過,但我想如果從另一個線程等待輸入並打印輸入時,如果您從一個線程打印某些內容,則等待輸入的輸入將獲得另一個線程作爲輸入的一部分輸出的內容。所以事情可能會變得混亂。就像我說的,雖然我沒有嘗試過。 – 2011-05-29 23:58:56

+0

@Doug:沒關係。只要確保兩個線程都不要嘗試同時使用相同的流。 – Nemo 2011-05-29 23:59:29

4

我已經在QNX4.5上做了這個不支持線程或者使用select來提升。您基本上通過select STDIN作爲要使用的文件描述符,並在進入新行時返回select。我在下面添加了一個簡化的示例循環。它與平臺無關,至少對於像Unix這樣的Unix系統而言。雖然不確定Windows。

while (!g_quit) 
{ 
    //we want to receive data from stdin so add these file 
    //descriptors to the file descriptor set. These also have to be reset 
    //within the loop since select modifies the sets. 
    FD_ZERO(&read_fds); 
    FD_SET(STDIN_FILENO, &read_fds); 

    result = select(sfd + 1, &read_fds, NULL, NULL, NULL); 
    if (result == -1 && errno != EINTR) 
    { 
     cerr << "Error in select: " << strerror(errno) << "\n"; 
     break; 
    } 
    else if (result == -1 && errno == EINTR) 
    { 
     //we've received and interrupt - handle this 
     .... 
    } 
    else 
    { 
     if (FD_ISSET(STDIN_FILENO, &read_fds)) 
     { 
     process_cmd(sfd); 
     } 
    } 
} 
+4

我聽說這種方法不適用於Windows。 – Doug 2011-05-29 23:59:55

+0

選擇是我的最愛。可以在Windows上運行cygwin或minsys lib。我認爲它必須奏效。我會試一試併發布結果。 – minghua 2013-09-11 14:07:15

0

StdinDataIO類BSD許可MUSCLE networking library的支持無阻塞從標準輸入Windows下讀取的MacOS/X和Linux/Unix的...你可以使用(或只是檢查代碼爲例如何做到這一點),如果你想。

0

您可以使用tinycon庫來執行此操作。只是在一個新線程中產生一個tinycon對象,而且你已經完成了很多工作。您可以定義觸發方法,以便在按下輸入按鈕時觸發您想要的任何內容。

你可以在這裏找到: https://sourceforge.net/projects/tinycon/

此外,許可證是BSD,所以這將是最寬鬆的滿足您的需求。

4

有一個簡單的方法:

char buffer[512]; 
int point = 0; 
... 
while (_kbhit()) { 
    char cur = _getch(); 
    if (point > 511) point = 511; 
    std::cout << cur; 
    if (cur != 13) buffer[point++] = cur; 
    else{ 
     buffer[point] = '\0'; 
     point = 0; 
     //Run(buffer); 
    } 
} 

無塊,都在1個線程。至於我,這是有效的。

+0

問題:這只是一個黑客,* _kbhit()*只返回*真*如果你使用你的硬件鍵盤。如果程序的輸入來自另一個進程,那麼* _kbhit()*塊。 – Tsoj 2017-06-16 17:13:31

0

libuv是一個用於異步I/O的跨平臺C庫。它使用事件循環來執行諸如從標準輸入讀取而不阻塞線程的操作。 libuv是Node.JS和其他人的力量。

3

非阻塞控制檯輸入C++? Answer:

Ans:在後臺線程上做控制檯IO,並提供線程之間通信的方法。

下面是一個完整的(但過於簡單)的測試程序,它通過將io推遲到後臺線程來實現異步io。

程序將等待您在控制檯上輸入字符串(以換行符結束),然後使用該字符串執行10秒的操作。

您可以在操作過程中輸入另一個字符串。

輸入'quit'讓程序在下一個週期停止。

#include <iostream> 
#include <memory> 
#include <string> 
#include <future> 
#include <thread> 
#include <mutex> 
#include <condition_variable> 
#include <deque> 

int main() 
{ 
    std::mutex m; 
    std::condition_variable cv; 
    std::string new_string; 
    bool error = false; 

    auto io_thread = std::thread([&]{ 
     std::string s; 
     while(!error && std::getline(std::cin, s, '\n')) 
     { 
      auto lock = std::unique_lock<std::mutex>(m); 
      new_string = std::move(s); 
      if (new_string == "quit") { 
       error = true; 
      } 
      lock.unlock(); 
      cv.notify_all(); 
     } 
     auto lock = std::unique_lock<std::mutex>(m); 
     error = true; 
     lock.unlock(); 
     cv.notify_all(); 
    }); 

    auto current_string = std::string(); 
    for (;;) 
    { 
     auto lock = std::unique_lock<std::mutex>(m); 
     cv.wait(lock, [&] { return error || (current_string != new_string); }); 
     if (error) 
     { 
      break; 
     } 
     current_string = new_string; 
     lock.unlock(); 

     // now use the string that arrived from our non-blocking stream 
     std::cout << "new string: " << current_string; 
     std::cout.flush(); 
     for (int i = 0 ; i < 10 ; ++i) { 
      std::this_thread::sleep_for(std::chrono::seconds(1)); 
      std::cout << " " << i; 
      std::cout.flush(); 
     } 
     std::cout << ". done. next?\n"; 
     std::cout.flush(); 
    } 
    io_thread.join(); 
    return 0; 
} 

樣品試運行:

$ ./async.cpp 
first 
new string: first 0 1las 2t 3 
4 5 6 7 8 9. done. next? 
new string: last 0 1 2 3 4 5 6 7 8quit 9. done. next? 
+3

你如何發出'io_thread'信號而不退出? 'getline'阻塞了 – dashesy 2016-10-20 01:22:46

+0

@dashesy如果我的要求比這更復雜,我可能會使用一些特定於平臺的代碼來處理I/O。 – 2016-10-20 07:01:01

0

示例使用C++ 11:

#include <iostream> 
#include <future> 
#include <thread> 
#include <chrono> 

static std::string getAnswer() 
{  
    std::string answer; 
    std::cin >> answer; 
    return answer; 
} 

int main() 
{ 
    int timeout = 5; 
    std::cout << "do you even lift?" << std::endl; 
    std::string answer = "maybe"; //default to maybe 
    std::future<std::string> future = std::async(getAnswer); 
    if (future.wait_for(std::chrono::seconds(timeout)) == std::future_status::ready) 
     answer = future.get(); 

    std::cout << "the answer was: " << answer << std::endl; 
    exit(0); 
} 

在線編譯:http://rextester.com/XGX58614