【问题标题】:Pthreads and signalsPthreads和信号
【发布时间】:2011-03-23 14:20:00
【问题描述】:

我在使用 pthread 时遇到了一点问题。基本上,我想捕获一个 SIGINT 并清理所有线程并退出。我有什么(骨架代码):

main.c:

sig_atomic_t running;

void handler(int signal_number)
{
    running = 0;
}

int main(void)
{
    queue job_queue = new_job_queue();

    running = 1;

    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = &handler;
    sigaction(SIGINT, &sa, NULL);

    /* create a bunch of threads */
    init_threads(&job_queue);

    while(running) {
        /* do stuff */
    }

    cleanup();

    return (0);
}

threads.c

extern sig_atomic_t running;
pthread_mutex_t queue_mutex = PTHREAD_MUTEX_INITIALIZER;
sem_t queue_count;

void init_threads(queue *q)
{
    int numthreads = 12; /* say */

    sem_init (&queue_count, 0, 0);

    pthread_t worker_threads[numthreads];

    int i;
    for(i=0;i<numthreads;i++)
        pthread_create(&worker_threads[i], NULL, &thread_function, q);
}

void * thread_function(void *args)
{
    pthread_detatch(pthread_self());

    queue *q = (queue *)args;

    while(running) {

        job *j = NULL;

        sem_wait(&queue_count);

        pthread_mutex_lock(&queue_mutex);
        j = first_job_in_queue(q);
        pthread_mutex_unlock(&queue_mutex);

        if(j) {
            /*do something*/
        }

    }

    return (NULL);
}

我在这方面运气不佳。由于您不能保证哪个线程会收到信号,所以我认为这是一个好方法。但是我遇到了一个问题,thread.c 中的sem_wait() 挂起,这是预期但不希望的。 threads.c 中的while(running) 循环似乎是多余的。我应该对 main 中的所有线程执行pthread_kill() 吗?上面的骨架代码有什么明显的问题吗?有没有更好/更简单的方法来做到这一点?

谢谢。

【问题讨论】:

    标签: c pthreads signals


    【解决方案1】:

    您可以做的是从处理程序调用sem_post(),直到所有线程都被解锁。在线程函数中,紧跟在sem_wait() 之后,您应该检查running 变量的值,以及它是否为零中断。

    处理程序中的代码可能类似于以下内容:

    int sval;    
    sem_getvalue(&queue_count, &sval);
    while (sval < 0) {
        sem_post(&queue_count);
        sem_getvalue(&queue_count, &sval);
    }
    

    当然应该验证返回值是否有错误

    【讨论】:

    • +1。这似乎是一个合理的方法。但是,我不确定使用 sem_getvalue 是最好的计划。根据手册页,如果有任何线程正在等待它,它可以在 sval 中放置一个值 0(这就是它在我正在使用的 Linux 版本上所做的)。似乎最好简单地调用 sem_post numthreads 次以保证所有工作线程都被释放。
    • +1 for sem_post 来自信号处理程序!这实际上是 POSIX 信号量的关键属性,使它们在互斥锁和 condvar 无法完成工作的情况下非常有用:sem_post唯一异步信号安全的 pthread 同步原语。
    【解决方案2】:

    你可以在一个线程中捕获SIGINT,并在所有其他线程中使用pthread_sigmask()阻塞SIGINT,如果SIGINT通过某种方式产生,信号将被传递到指定线程,该线程可以调用pthread_cancel() 取消所有其他线程。

    【讨论】:

      【解决方案3】:

      您可能需要考虑在每次调用 pthread_create 后调用 pthread_join。这将允许您的主线程等待所有线程完成执行。

      但也许我有点误会了...您是要等待所有线程完成,还是只是等待一个完成,然后立即停止所有其他线程?

      【讨论】:

      • jmoney38:我实际上是在 for 循环中尝试pthread_join(worker_threads[i], NULL),但它导致循环在第一个循环之后停止。我不确定我做了什么。我想等到线程完成是更好的方法。
      • 对,所以首先循环并创建,然后再次循环并在阵列中的每个线程上执行 pthread_join。是的,它会等待 0,然后是 1,然后是 2……但是如果后面的线程先完成,这不是问题,它只会被“跳过”。也就是说,在已经完成的线程上调用 pthread_join 没有错误。
      • 啊,所以我在init_threads() 函数中执行pthread_create(),然后在main 末尾执行pthread_join()(或者在我拥有的清理函数中)。
      【解决方案4】:

      如果你没有必要,你不应该做pthread_kill()。我对pthread_detatch() 不熟悉,但如果您希望您的main() 函数等待线程完成,那么如果您的cleanup() 函数在每个线程ID 上执行pthread_join() 可能会更好从pthread_create()返回,等待每个线程正常退出。

      另外,据我所知,sem_wait() 挂起,因为您的信号量值已初始化为 0。如果您想说最多 5 个线程一次访问共享资源,请将信号量初始化为 5,即sem_init(&amp;queue_count, 0, 5).

      【讨论】:

      • dave-rager:我正在使用信号量和互斥锁,因为作业队列可能会变空。因此,当我从队列中取出下一个作业时,我会锁定互斥锁,但是当队列为空时,信号量会在那里等待作业再次可供处理。
      • 我明白了。那么大概 main() 中的 while 循环正在排队作业并发布信号量?
      • 是的,完全正确。我应该提到这一点,抱歉。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-01-05
      • 2014-08-29
      • 2023-03-22
      • 1970-01-01
      • 2013-07-04
      • 2011-07-07
      相关资源
      最近更新 更多