2014-10-27 110 views
4

我有一个多线程的应用程序,它使用单个线程上的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()构建一种帧限制器吗?还是会过度使用?

+0

你就不能使用条件变量让如发生改变诅咒线程知道和需要刷新,只是有它坐在等候呢?如果数据只是不断更新,那么是的,只是睡一会儿就没问题。 – 2014-10-27 01:32:27

+0

不,我不能在用户交互之间发生变化。 – 2014-10-27 01:34:24

+0

然后,最简单的方法可能是'select()',并且超时时间适当。当'select()'返回时,无论是因为有用户输入还是超时,都要在这一点上进行刷新。 – 2014-10-27 01:39:08

回答

3

根据评论,如果在刷新之间需要处理用户输入,那么最简单的方法可能是在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(); 
} 
+0

看起来'select()'只有在设置足够低的超时时才会返回。这很奇怪......我不能在90分钟内提出另一个问题。这是一个记事本链接,用于编码错误发生的位置。如果超时足够高,奇怪的行为不会发生。这里是一个可编辑的检查:http://shrib.com/0yKkHary – 2014-10-27 02:49:32

+0

@ rowan.G:查看更新的问题的例子。你的代码有一些问题,你不会每次都重置'fd_set',而且你调用'getch()',而不管select()'指示输入是否准备好。你也不会在你的循环中的任何地方调用'refresh()',这可能没有帮助。看看你如何继续我的示例代码。 – 2014-10-27 04:36:57

+0

谢谢,我没有意识到我每次调用'select()'时都会重置'FD_SET()'。 'getch()'隐式调用'refresh()' – 2014-11-03 11:03:47