ncurses在多线程应用程序
我有一个多线程的应用程序,它使用单个线程上的ncurses向用户报告信息。我的代码基本上是这样的:ncurses在多线程应用程序
const unsigned int refresh_cycle = 180;
unsigned int refresh_count = refresh_cycle;
while(killswitch != 1) {
if (refresh_count >= refresh_cycle) {
// critical section which obtains some data worked on by a thread. only does this once every refresh cycle times
// mtx lock, fetch, mtx unlock
refresh_count = 0;
}
refresh_count++;
// get input
// draw some stuff
// refresh
}
我注意到的是,ncurses的窗口得到刷新很多很多倍。对于一个只需15-30次刷新即可完成的用户来说,这种方式实际上是非常需要的。
但现在我担心这可能会从正在工作的线程中“偷走”不必要的处理能力。这是一个合理的断言吗?
我应该使用usleep()
构建一种帧限制器吗?还是会过度使用?
根据评论,如果在刷新之间需要处理用户输入,那么最简单的方法可能是在STDIN_FILENO
上调用select()
并使用适当的小超时。当select()
返回时,要么是因为有用户输入,要么是因为超时,请在该点进行刷新。
下面是一个示例,可让您了解如何设置它,并显示select()
何时返回以及发生多少次,以便您可以直观地看到发生了什么。尝试让它坐下并运行一段时间,然后尝试按住一个键,然后观察select() has returned [n] times
消息在每种情况下的行为。在代码中的注释说明了发生的事情:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include <sys/select.h>
#include <ncurses.h>
/* struct to store curses info for cleanup */
struct curinfo {
WINDOW * main_window;
int old_cursor;
};
/* curses helper functions */
void start_curses(struct curinfo * info);
void stop_curses(struct curinfo * info);
/* main function */
int main(int argc, char * argv[])
{
/* Set default timeout */
int secs = 0;
int usecs = 500000;
/* Set timeout based on command line args, if provided */
if (argc > 1) {
if (!strcmp(argv[1], "veryshort")) {
secs = 0;
usecs = 200000;
}
else if (!strcmp(argv[1], "short")) {
secs = 1;
usecs = 0;
}
else if (!strcmp(argv[1], "medium")) {
secs = 2;
usecs = 0;
}
else if (!strcmp(argv[1], "long")) {
secs = 5;
usecs = 0;
}
}
struct curinfo cinfo;
start_curses(&cinfo);
int input = '0'; /* Set to something printable */
int num_sel = 0; /* Number of times select() has returned */
while (input != 'q' && input != 'Q') {
/* Output messages */
mvprintw(3, 3, "select() has returned %d times", num_sel);
mvprintw(4, 3, "Last character input was %c", input);
mvprintw(5, 3, "Press 'q' to quit");
refresh();
/* select() modifies the fd_sets passed to it,
* so zero and set them prior to each call. */
fd_set fds;
FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds);
/* Same deal for the struct timeval, select() may
* modify it, it may not, so recreate to be portable. */
struct timeval tv;
tv.tv_sec = secs;
tv.tv_usec = usecs;
/* Store the return so we can check it */
int status = select(STDIN_FILENO + 1, &fds, NULL, NULL, &tv);
/* Check for error */
if (status == -1) {
/* select() returned with an error. */
if (errno != EINTR) {
/* If interrupted by a signal, no problem,
* keep going. Otherwise, let's just quit. */
stop_curses(&cinfo);
perror("error calling select()");
return EXIT_FAILURE;
}
}
else if (FD_ISSET(STDIN_FILENO, &fds)) {
/* Only call getch() if input is ready.
* getch() will not block when we do it this way. */
if ((input = getch()) == ERR) {
stop_curses(&cinfo);
fprintf(stderr, "ERR returned from getch()\n");
return EXIT_FAILURE;
}
}
/* Increment number of times select() has returned */
++num_sel;
}
stop_curses(&cinfo);
return 0;
}
/* Starts curses and populates the passed struct */
void start_curses(struct curinfo * info)
{
if ((info->main_window = initscr()) == NULL) {
fprintf(stderr, "Error calling initscr()\n");
exit(EXIT_FAILURE);
}
keypad(stdscr, TRUE);
timeout(0);
raw();
nonl();
noecho();
info->old_cursor = curs_set(0);
refresh();
}
/* Stops curses and cleans up */
void stop_curses(struct curinfo * info)
{
delwin(info->main_window);
curs_set(info->old_cursor);
endwin();
refresh();
}
看起来'select()'只有在设置足够低的超时时才会返回。这很奇怪......我不能在90分钟内提出另一个问题。这是一个记事本链接,用于编码错误发生的位置。如果超时足够高,奇怪的行为不会发生。这里是一个可编辑的检查:http://shrib.com/0yKkHary – 2014-10-27 02:49:32
@ rowan.G:查看更新的问题的例子。你的代码有一些问题,你不会每次都重置'fd_set',而且你调用'getch()',而不管select()'指示输入是否准备好。你也不会在你的循环中的任何地方调用'refresh()',这可能没有帮助。看看你如何继续我的示例代码。 – 2014-10-27 04:36:57
谢谢,我没有意识到我每次调用'select()'时都会重置'FD_SET()'。 'getch()'隐式调用'refresh()' – 2014-11-03 11:03:47
你就不能使用条件变量让如发生改变诅咒线程知道和需要刷新,只是有它坐在等候呢?如果数据只是不断更新,那么是的,只是睡一会儿就没问题。 – 2014-10-27 01:32:27
不,我不能在用户交互之间发生变化。 – 2014-10-27 01:34:24
然后,最简单的方法可能是'select()',并且超时时间适当。当'select()'返回时,无论是因为有用户输入还是超时,都要在这一点上进行刷新。 – 2014-10-27 01:39:08