在多线程应用程序中进行ncurses

时间:2014-10-27 01:26:14

标签: c multithreading 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()构建一种帧限制器,还是会过度使用?

1 个答案:

答案 0 :(得分:4)

根据评论,如果在刷新之间需要处理用户输入,那么最简单的方法可能是在select()上使用适当小的超时调用STDIN_FILENO。当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();
}