这就是 http://invisible-island.net/ncurses/ncurses.faq.html#multithread
上所说的内容如果您的程序在多个线程中使用诅咒,您几乎肯定会看到奇怪的行为。这是因为curses 依赖于输入和输出的静态变量。使用一个线程进行输入,使用其他线程进行输出并不能解决问题,额外的屏幕更新也无济于事。此常见问题解答不是有关线程编程的教程。
具体来说,它提到即使输入和输出在单独的线程上完成也是不安全的。如果我们进一步对整个 ncurses 库使用互斥锁,以便一次最多有一个线程可以调用任何 ncurses 函数,这样会安全吗?如果没有,在多线程应用程序中安全使用 ncurses 的其他廉价解决方法是什么?
我问这个问题是因为我注意到真实的应用程序通常有自己的事件循环,但依赖 ncurses
getch
函数来获取键盘输入。但如果主线程在自己的事件循环中阻塞等待,那么它就没有机会调用getch
。一个看似适用的解决方案是在不同的线程中调用getch
,这还没有给我带来问题,但正如上面所说的实际上并不安全,并且已被另一个用户here验证。所以我想知道将 getch
合并到应用程序自己的事件循环中的最佳方法是什么。
我正在考虑使
getch
非阻塞并定期(每 10-100 毫秒)唤醒主线程以检查是否有内容要读取。但这会增加关键事件之间的额外延迟,并使应用程序的响应速度降低。另外,我不确定这是否会导致某些 ncurses 内部延迟(例如 ESCDELAY
)出现任何问题。
我正在考虑的另一个解决方案是直接轮询标准输入。但我想 ncurses 也应该做类似的事情,从两个不同的地方读取相同的流看起来很糟糕。
文本还提到了“ncursest”或“ncursestw”库,但它们似乎不太可用,例如,如果您使用不同的诅咒语言绑定。如果标准 ncurses 库有一个可行的解决方案,那就太好了。
如果没有线程支持,您将无法在多个线程中使用curses 函数。 这是因为大多数诅咒调用都使用静态或全局数据。 例如,
getch
函数调用refresh
,它可以使用全局指针curscr
和stdscr
来更新整个屏幕。 线程支持配置的区别在于全局值被转换为函数并添加了互斥体。
如果您想从不同的线程读取 stdin 并在一个线程中运行 curses,您可能可以通过检查 文件描述符(即
0
)中的待处理活动来实现这一点,并且警告运行curses的线程告诉它读取数据。
通常在 GUI 应用程序中,主线程负责 IO 和显示。产生的每个其他线程都用于执行后台工作并通知上面的线程进行 UI 更新等。
这不仅适用于curses(shell)应用程序,而且是适用于Winforms、WPF、MFC等的基本设计规则。
现在,您最好的选择确实是通过使用使 fetch 非阻塞
nodelay(stdscr, true);
关于延迟,您指的是最广泛使用的方法,它也适合您的要求,就像
clock_t last_print_time = clock();
const int delay = 100000; // 100 milliseconds
while (1) {
ch = getch();
if (ch != ERR) { // If a key is pressed
printw("Key pressed: %c\n", ch);
refresh();
last_print_time = clock(); // Reset the timer after a key press
}
// Check if the delay time has passed
if ((clock() - last_print_time) * 1000000 / CLOCKS_PER_SEC >= delay) {
// Perform any periodic tasks here
last_print_time = clock(); // Reset the timer
}
usleep(10000); // Sleep for a short time to prevent high CPU usage
}
上面的方法(简而言之)就是上面提到的框架和游戏引擎处理这个问题的方法。