c - 多线程应用程序中的 ncurses

标签 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() 构建一种帧限制器,否则会过火吗?

最佳答案

根据评论,如果在刷新之间您需要处理用户输入,那么最简单的方法可能是在 STDIN_FILENO 上调用 select()适当的小超时。当 select() 返回时,无论是因为有用户输入还是因为它超时,在那个时候进行刷新。

下面是一个示例,可让您了解如何进行设置,并显示 select() 返回的时间和次数,以便您可以直观地了解正在发生的事情。尝试让它运行一段时间,然后尝试按住一个键,观察 select() 已返回 [n] 次 消息在每种情况下的行为。代码中的注释解释了正在发生的事情:

#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();
}

关于c - 多线程应用程序中的 ncurses,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26579937/

相关文章:

c - 有没有一种可移植的方法来在 Makefile 中创建可移植的可选依赖项?

c - pthread_cond_wait 无限等待的可能原因

c++ - 多个与单个条件变量

ruby - 我如何使用 ncurses 在 telnet 界面上绘制 ..?

c - 从 sshell 读取符号...(未找到调试符号)...完成。 (是的,我编译时用了-g)

c - 如何在Raspberry Pi上运行Azure IoT C SDK的端到端测试?

java - 守护进程线程数、线程数和总启动线程数

c#为什么要把对象放在lock语句中

c - 在窗口内移动光标 - ncurses

linux - 哪些 ncurses 框架可用于 BASH?