2014-10-27 113 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