1

我有一个在标准输出上打印的控制台应用程序。我想实现以下行为:

  • 输出一圈打印(每圈后休息 2 秒)
  • 用户可以(对他来说是可选的)按一个键开始打印其他数据
  • 当用户不点击任何东西时,只打印输出,没有任何变化
  • 当用户单击预定义的键时(即使在打印输出期间他也可以这样做)应该调用一些函数

起初我考虑在其他线程中执行此操作(一个用于打印,一个用于等待输入),但我认为在这种情况下它没有用,因为在线程中等待输入是不可能的。

所以我找到了两个可能能够做到这一点的库:

我不知道他们中的任何一个,所以我不确定我应该学习哪一个来获得目标。或者也许还有另一个更简单的解决方案?

操作系统:Unix

编辑: g-makulik 让我展示,为什么不可能使用线程坚持写,这是可能的,确实是这样,但我不认为这应该是这样的:

#include <pthread.h>
#include <iostream>
#include <string>
#include <stdio.h>

void* print_message_function(void *doPrint) {
    bool* vDoPrint = (bool*) doPrint;
    while (*vDoPrint) {
        sleep(0.5);
        std::cout << "Thread 1" << std::endl;
    }
    return NULL;
}

void* keyPressed(void* doPrint) {
    bool* vDoPrint = (bool*) doPrint;
    while (*vDoPrint) {
        *vDoPrint = (char) getchar() == 'k' ? false : true;
        std::cout << "THIS ISN'T DISPLAYED UNLESS 'k' PRESSED.";
    }
    return NULL;
}

int main(int argc, char* argv[]) {
    pthread_t thread1, thread2;
    int iret1, iret2;
    bool doPrint = true;
    iret2 = pthread_create(&thread2, NULL, print_message_function,
            (void*) &doPrint);
    iret1 = pthread_create(&thread1, NULL, keyPressed,
            (void*) &doPrint);

    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

    std::cout << "Thread 1 returns: " << iret1 << std::endl;
    std::cout << "Thread 2 returns: " << iret2 << std::endl;
    return 0;
}
4

3 回答 3

1

可以等待一个线程的键盘输入,而另一个线程输出到同一个控制台。你只需要同步你的线程(输入/输出)来获得用户输入的功能(例如使用事件队列)。

编辑:在提供这些系统调用的系统上, 使用select()或使用超时而不是单独的线程可能是更好的选择。epoll()

ncurses 库将使您能够以类似窗口的方式组织控制台显示(例如,具有单独的面板来显示输出并具有某种命令(行)输入)。有关ncurses 能够做什么的高级示例,请参阅午夜指挥官。

但只要您不关心应用程序中回显输入的这种分离,输入和输出的简单线程分离就可以正常工作。

我不能告诉任何关于 SMFL 的信息,但这个库似乎支持图形级别的窗口而不是控制台。

于 2012-08-22T18:38:07.530 回答
0

我之前使用过 ncurses,虽然下面的解决方案可能不是最漂亮的,但它应该可以工作。

Ncurses 允许您将输入置于“无延迟”模式。这使得getch()从输入中读取字符的 非阻塞,如果用户没有按下任何东西,它将立即返回 ERR。

您可以等待标准输入文件描述符以查看输入文件描述符是否已准备好读取。也就是说,select()或者epoll()你想要的任何东西。如果你必须每 2 秒做一次,你可以把那个时间放到select()epoll()。如果你确实得到了按键,那么你可以决定你想用它做什么。

该解决方案不需要多线程。

我不了解 SFML,所以我不知道它是否会使这更容易。Ncurses 是一个 C 库,对于 C++ 应用程序可能方便也可能不方便。如果您安装了手册页,您可能可以直接查看任何 ncurses 函数的文档(例如,man getch)。

于 2012-08-22T18:35:46.853 回答
0

SFML是我以前从未听说过的东西。我不能说这是一个糟糕的选择,而是一个非常不受欢迎的选择,因此它很可能会导致您的程序的用户不得不为它安装它。我不会走那条路。

ncurses是一个非常常见且很好的解决方案,但我认为它对您的情况来说太复杂了。当您进行更复杂的文本 I/O 时,ncurses 非常方便,例如在屏幕上绘图、组装窗口、处理鼠标等。对于这样一个简单的应用程序,我认为它可能有点矫枉过正。另一方面,如果您想捕捉非常特定的按键(例如箭头键或其他不会导致文本输入的键)或处理鼠标,那么 ncurses 将帮助您。

另一个流行且轻量级的选择是SDL(及其 SDL_input 库)。与之前的解决方案不同,它完全是面向输入的。它将允许您以非阻塞方式非常详细地捕捉击键、鼠标点击和可能的其他输入事件。但是,您通常将 libsdl 安装为一个整体,虽然它很小,但我不确定您的应用程序的所有用户是否都会喜欢它。好吧,除非他们使用通常使用 SDL 的游戏。

最后,您可以自己实现这样一个简单的事情。如果您只关心 *nix(而不关心 Windows 等系统的可移植性),您可以设置stdin为非阻塞模式,并在主循环中使用重复的非阻塞读取来检查按键。如果您的应用程序在循环迭代之间的等待比实际处理更多,则最好使用它select()来处理延迟和等待输入。

我会避免仅出于这个原因引入线程,因为它们效率低下并且通常会引入比它们解决的问题更多的问题。如果非阻塞 I/O 可行,则永远不要使用线程阻塞 I/O。

于 2012-08-22T18:38:52.610 回答