c - 线程间setjmp/longjmp处理超时

标签 c multithreading pthreads setjmp

我正在将软件从嵌入式计算机移植到 Linux 机器上。 (Ubuntu 14.04 或 Raspbian (raspberry pi))

原始程序使用 setjmp/longjmp 来处理超时和 CTRL+C 事件。它在具有单个主线程(一个线程)的微 Controller 上运行。

我试图在使用线程 (pthreads) 时有类似的行为。

我的想法是我想要超时或 CTRL+C 来重新启动无限循环。

原始代码的作用类似于下面的代码。我不介意通过其他方式删除 setjmp/longjmp。 (例如:try/catch 或 signal 或 pthread_kill、条件变量等。)

知道如何使用 C/C++ 实现类似的行为吗?

这里是似乎部分工作的代码,可能不推荐/损坏:

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <setjmp.h>

// Define
#define TICK_NS_TIME (10000000)                                                 // 0.01 sec = 10 ms (100 times per second)
#define NS_PER_SEC   (1000000000)                                               // Nano sec per second.
#define TICK_PER_SEC (NS_PER_SEC/TICK_NS_TIME)                                  // Number of tick per second (Ex:100)
#define TIMEOUT_COUNT (30*TICK_PER_SEC)                                         // 30 seconds timeout (with 100 tick per second)

// Env set/long jmp
#define ENV_SZ (2)
#define ENV_TIMEOUT (0)
#define ENV_CTRLC (1)
static jmp_buf env[ENV_SZ];

// Variables
int timeout_val;


// sig handler.
void signal_handler(int signo)
{
    pthread_t self = pthread_self();
    printf("Thread %lu in signal handler\n", (long)self);
    if (signo == SIGINT) {
        longjmp(env[ENV_CTRLC], 1);                                             // Q?: Is it in the same thread ? (Never, Always, Sometimes?)
    }
    else 
    {
        printf("Other signal received..quitting.");                             // Ex: kill -9 pid
        exit(0);
    }
}

// thread timer function
void* timer_function(void* in_param)
{
    // Loop approx 100x per second.
    for (;;) {
        nanosleep((const struct timespec[]){{0, TICK_NS_TIME }}, NULL);         // Sleep 10 ms seconds.
        if (timeout_val) {
            if (!--timeout_val) {
                longjmp(env[ENV_TIMEOUT], 1);                                   // longjmp when timer reaches 0. (Q?: Is this valid with multithread?)
            }
        }
    }
}

// main
int main(int argc, char **argv)
{
    int i;
    int val;
    struct sigaction actions;
    pthread_t thread;
    setvbuf (stdout, NULL, _IONBF, 0);                                          // Make sure stdout is not buffered (ex:printf, etc.)
    printf("[Program started]\r\n");

    memset(&actions, 0, sizeof(actions));
    sigemptyset(&actions.sa_mask);
    actions.sa_flags = 0;
    actions.sa_handler = signal_handler;
    val = sigaction(SIGINT, &actions, NULL);  
    pthread_create(&thread, NULL, timer_function, NULL);                        // timer thread for example
    printf("[Timer thread started]\r\n");

    // setting env.
    val = setjmp(env[ENV_TIMEOUT]);
    if (val!=0){ printf("[JMP TIMEOUT]\r\n"); }

    val = setjmp(env[ENV_CTRLC]);
    if (val!=0){ printf("[JMP CTRLC]\r\n"); }

    // main loop
    timeout_val = TIMEOUT_COUNT;
    i = 0;
    for (;;)
    {
        i++;
        if (i > 10){ i = 0; printf("[%d]", timeout_val/TICK_PER_SEC); }         // Number of seconds before time out.
        sleep(1);
        printf(".");
    }
    printf("Main completed\n");
    return 0;
}
//Compile: g++ -pthread main.cpp -o main

替代实现的建议会很好,因为我是线程编程的新手!

最佳答案

setjmp()保存了恢复调用环境所需的信息。 longjmp() 可以恢复这个环境,但只能在同一个线程内。

C11 标准明确规定了具有相同线程的约束:

7.13.2.1/2 If there has been no such invocation (i.e: of a previous setjmp), or if the invocation was from another thread of execution, or if the function containing the invocation of the setjmp macro has terminated execution in the interim, or if the invocation of the setjmp macro was within the scope of an identifier with variably modified type and execution has left that scope in the interim, the behavior is undefined.

实际上,setjmp/longjmp 通常是通过保存堆栈指针来实现的,因此只有在相同的执行上下文中才能恢复它。

备选

除非我遗漏了什么,否则您只需将第二个线程用作计时器。您可以改为摆脱 POSIX pthread,并使用由 POSIX timer_create() 激活的计时器信号。

但请注意,在信号处理程序中使用 setjmp/longjmp(因此在您的 CTRL+C 的原始代码中已经存在)很棘手,如 SO answer 中所述。所以你会考虑 sigsetjmp/siglongjmp

备案:C 还是 C++?

您的问题被标记为 C。但是您提到了 C++ try and catch。所以为了完整起见:

  • 在 C++ 中,setjmp 应该被替换为 try/catchlongjmp 并抛出异常。 setjmp/longjmp 仅在展开堆栈不需要调用任何非平凡析构函数时才在 C++ 中受支持(参见 C++ 标准,18.10/4)。
  • 异常不会跨线程传播,除非使用 std::rethrow_exception() 捕获并明确重新抛出。它很脆弱,因此请参阅 this SO question 了解更多详细信息。但这是可能的,并且可以解决您的问题。

关于c - 线程间setjmp/longjmp处理超时,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38234546/

相关文章:

c - 在线程之间共享相同的 epoll 文件描述符可以吗?

c - C 中的参数和返回数组

java - 变量在静态时发生变化,在非静态时保持不变

c++ - 你如何获得当前信号量的计数?

multithreading - 对于多线程视频教练程序来说,这是一个合适的结构吗?

c - 如何使用 pthread_cond_signal 和 pthread_cond_wait 使 pthreads 工作正常?

每次使用 pthread 调用方法时创建一个新线程

c - 当我为内部有 union 的嵌套结构分配和释放内存时出了什么问题

c - long int 的大小差异

c - 使用二分查找查找已排序字符串中的第一个大写字母