首页 > 解决方案 > 终止具有临界区代码的 POSIX 多线程应用程序的最佳方法是什么?

问题描述

我正在开发的应用程序恰好是多线程的,每个线程都有一个临界区代码。当用户中断应用程序时,我需要在终止应用程序之前终止线程并保存执行状态。为了实现这一点,我在线程函数的随机位置编写了一些检查。下面是有助于理解执行流程的最少代码。

#include<pthread.h>
#include<signal.h>
#include<stdlib.h>

struct thread_data
{
    int quit;
    /* other data variables */
};

void* thread_func(void* data)
{

    for ( ; ; )
    {

        /* Non critical section code start */

        if (((struct thread_data*) data)->quit)   // checks at random places
            pthread_exit(NULL);

        /* end */

        if (((struct thread_data*) data)->quit)
            pthread_exit(NULL);

        /* Critical section code start */

            // Use data{} structure.

        /* end */

        if (((struct thread_data*) data)->quit)
                pthread_exit(NULL);
    }
}

int main()
{
    sigset_t sigmask;
    sigemptyset(&sigmask);
    sigaddset(&sigmask, SIGINT);

    pthread_sigmask(SIG_BLOCK, &sigmask, NULL);  // SIGINT is blocked by all the threads.


    struct thread_data* data = calloc(5, sizeof(struct thread_data));
    pthread_t tids[5];

    for (int i = 0; i < 5; i++)     // initialize the threads.
        pthread_create(tids + i, NULL, thread_func, (void*) (data + i));

    int signo;
    sigwait(&sigmask, &signo);  // wait for user interrupt.

    for (int i = 0; i < 5; i++)     // terminate threads.
    {
        data[i].quit = 1;
        pthread_join(tids[i], NULL);
    }

    /* Save the execution state */

        // Use data{} structure variable

    return 0;
}

但是这种方法似乎并不熟练,当thread_func规模扩大时,将这些检查放在多个地方变得很累。还有一点要提一下,我不能依赖信号处理和pthread_exit()从信号处理程序调用,因为它不是async-signal-safe function。有没有更好的方法来实现这一目标?

标签: clinuxmultithreadingpthreadsposix

解决方案


可能不是您正在寻找的东西,也不是真正具有开创性的东西。但是要删除一些文本(因为我同意它看起来有点乱)至少声明一个指针而不是一直强制转换。

void* thread_func(void* data)
{
    struct thread_data *d = (struct thread_data*) data;

    if(d->quit) pthread_exit(NULL);

如果你经常做这些检查,这会让它更干净。你甚至可以让它更干净,int *quit = &d->quit但也许这有点过头了。

或者使用函数或宏:

void maybe_quit(int x) 
{
    if(x) pthread_exit(NULL);
}

#define MAYBE_QUIT do {  if (((struct thread_data*) data)->quit) \
                             pthread_exit(NULL); }               \
                   while(0)

不是真正的创新方法,但它肯定会使代码看起来更干净。

当我必须进行大量错误检查并且我发现必须调试或分析退出检查功能的可能性非常低时,我会选择一个宏。我认为这更容易阅读:

void* thread_func(void* data)
{
    for ( ; ; )
    {
        /* Non critical section code start */

        MAYBE_QUIT;

        /* end */

        MAYBE_QUIT;

        /* Critical section code start */

            // Use data{} structure.

        /* end */

        MAYBE_QUIT;
    }
}

当您一遍又一遍地阅读代码时,差异实际上是相当大的。大脑可以很快学会忽略那些大写字母。


推荐阅读