我正在尋找一種(多平臺)方式爲我的C++程序執行非阻塞控制檯輸入,因此我可以在程序不斷運行時處理用戶命令。該計劃也將同時輸出信息。非阻塞控制檯輸入C++
什麼是最好的/最簡單的方法來做到這一點?使用外部庫如boost,只要他們使用許可許可證,就沒有問題。
我正在尋找一種(多平臺)方式爲我的C++程序執行非阻塞控制檯輸入,因此我可以在程序不斷運行時處理用戶命令。該計劃也將同時輸出信息。非阻塞控制檯輸入C++
什麼是最好的/最簡單的方法來做到這一點?使用外部庫如boost,只要他們使用許可許可證,就沒有問題。
我會通過創建一個獨立的線程來調用普通的阻塞IO函數,並將它傳遞給一個回調函數,它會在輸入時調用它。你確定你需要做你說的你想做的事嗎?
至於在同一時間輸出信息,如果用戶正在鍵入某些輸入和打印某些內容時會發生什麼?
如果我有一個線程等待說... cin處理輸入,然後另一個線程會發生什麼線程使用cout輸出控制檯?那會很糟嗎? – Doug 2011-05-29 23:53:59
@Doug你不應該真的在輸出和從控制檯輸入/從不同的線程(除非你想有一個同步對象來保持它們一致,這可能會使你首先使用線程的原因順其自然)。我以前沒有嘗試過,但我想如果從另一個線程等待輸入並打印輸入時,如果您從一個線程打印某些內容,則等待輸入的輸入將獲得另一個線程作爲輸入的一部分輸出的內容。所以事情可能會變得混亂。就像我說的,雖然我沒有嘗試過。 – 2011-05-29 23:58:56
@Doug:沒關係。只要確保兩個線程都不要嘗試同時使用相同的流。 – Nemo 2011-05-29 23:59:29
ncurses可以是一個很好的候選人。
我已經在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);
}
}
}
的StdinDataIO類BSD許可MUSCLE networking library的支持無阻塞從標準輸入Windows下讀取的MacOS/X和Linux/Unix的...你可以使用(或只是檢查代碼爲例如何做到這一點),如果你想。
您可以使用tinycon庫來執行此操作。只是在一個新線程中產生一個tinycon對象,而且你已經完成了很多工作。您可以定義觸發方法,以便在按下輸入按鈕時觸發您想要的任何內容。
你可以在這裏找到: https://sourceforge.net/projects/tinycon/
此外,許可證是BSD,所以這將是最寬鬆的滿足您的需求。
有一個簡單的方法:
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個線程。至於我,這是有效的。
問題:這只是一個黑客,* _kbhit()*只返回*真*如果你使用你的硬件鍵盤。如果程序的輸入來自另一個進程,那麼* _kbhit()*塊。 – Tsoj 2017-06-16 17:13:31
libuv是一個用於異步I/O的跨平臺C庫。它使用事件循環來執行諸如從標準輸入讀取而不阻塞線程的操作。 libuv是Node.JS和其他人的力量。
非阻塞控制檯輸入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?
你如何發出'io_thread'信號而不退出? 'getline'阻塞了 – dashesy 2016-10-20 01:22:46
@dashesy如果我的要求比這更復雜,我可能會使用一些特定於平臺的代碼來處理I/O。 – 2016-10-20 07:01:01
示例使用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);
}
可以爲你一個普通的線程庫的工作? – Steinbitglis 2011-05-29 23:40:56
@Steinbitglis:什麼是「普通」線程庫,它與其他任何線程庫有什麼不同? – 2011-05-29 23:46:18
@Tomalak我認爲他的意思是一個線程庫,一個非阻塞的IO庫。 – 2011-05-29 23:47:26