通过 pthread_cond_broadcast 触发多个 pthread

Trigger multiple pthreads by pthread_cond_broadcast

由于带有 pthread_cond_broadcast 唤醒的 pthreads 的例子很少,我写了一个,但不确定这是否正确同步以及实现它的方法:

  1. 是否所有线程共享相同的 c 和 mtx 变量?
  2. 是否有必要在 pthread_cond_wait return 时测试是否确实满足某些条件?在我的例子中,每次广播调用都应该恰好唤醒每个线程一次,但其他人不应该这样做。 (我能防止虚假唤醒吗?)
  3. 尽管异步取消类型,程序当前不会退出。尽管 pthread_cond_wait 是取消点 so,但在示例代码中尝试延迟取消也没有成功。

总的来说它是否像我期望的那样工作。


 #include <pthread.h> 
    #include <iostream>
    #include <unistd.h>

    struct p_args{
        int who;
    };

    pthread_cond_t  c;      //share between compilation units
    pthread_mutex_t mtx;

    void *threadFunc(void *vargs){
        //pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
        struct p_args * args = (struct p_args *) vargs;

        while(true){
            //wait for trigger one loop
            pthread_mutex_lock(&mtx);
            pthread_cond_wait(&c, &mtx);
            pthread_mutex_unlock(&mtx);
            //should be entangled output showing concurrent execution
            std::cout << "t " << args->who << std::endl;

            /* expensive work */
        }
        delete args;
    }

    int main(int argc, char* argv[])
    {
        pthread_cond_init(&c, NULL);
        pthread_mutex_init(&mtx, NULL);

        pthread_t thread_id[2];

        struct p_args *args0 = new p_args();
        struct p_args *args1 = new p_args();
        args0->who = 0;
        args1->who = 1;

        pthread_create(&thread_id[0], NULL,  threadFunc,    args0);
        pthread_create(&thread_id[1], NULL,  threadFunc,    args1);

        sleep(3);
        pthread_mutex_lock(&mtx);
        pthread_cond_broadcast(&c);
        pthread_mutex_unlock(&mtx);
        sleep(3);//test if thread waits  
        pthread_cancel(thread_id[0]);
        pthread_cancel(thread_id[1]);

        pthread_join (thread_id[0], NULL);
        pthread_join (thread_id[1], NULL);
        //could perform cleanup here
        return 0;
    }

Regarding exiting deferred:
thread_id[0] exits fine and i am stuck in line `pthread_join (thread_id[1], NULL);`, it says (Exiting) but seems stuck on a lock, with debugger:  
<br>
[![enter image description here][2]][2]
<br>

编辑 我想出的最终解决方案:

#include <pthread.h>
#include <iostream>
#include <unistd.h>

struct p_args{
    int who;
};

pthread_cond_t  c;
pthread_mutex_t mtx;
bool doSome[2];
bool exitFlag;

void *threadFunc(void *vargs){
    struct p_args * args = (struct p_args *) vargs;

    while(true){
        //wait for trigger one loop
        pthread_mutex_lock(&mtx);
        do {
            pthread_cond_wait(&c, &mtx);
            if(exitFlag) {
                std::cout << "return " << args->who << std::endl;
                delete args;
                pthread_mutex_unlock(&mtx);
                return NULL;
            }
        } while(doSome == false);
        doSome[args->who] = false;
        pthread_mutex_unlock(&mtx);
        std::cout << "t " << args->who << std::endl;
    }
}

int main(int argc, char* argv[])
{
    pthread_cond_init(&c, NULL);
    pthread_mutex_init(&mtx, NULL);
    pthread_t thread_id[2];

    struct p_args *args0 = new p_args();
    struct p_args *args1 = new p_args();
    args0->who = 0;
    args1->who = 1;

    doSome[0] = doSome[1] = true;
    exitFlag = false;
    pthread_create(&thread_id[0], NULL,  threadFunc,    args0);
    pthread_create(&thread_id[1], NULL,  threadFunc,    args1);

    doSome[0] = doSome[1] = true;
    pthread_cond_broadcast(&c);
    sleep(3);
    doSome[0] = doSome[1] = true;
    pthread_cond_broadcast(&c);
    sleep(3);
    exitFlag = true;
    pthread_cond_broadcast(&c);

    pthread_join (thread_id[0], NULL);
    pthread_join (thread_id[1], NULL);

    return 0;
}
  1. do all threads share the same c and mtx variable?

是的,就像任何其他全局变量一样。您可以从每个线程打印他们的地址以确认它。

  1. is it necessary upon pthread_cond_wait return to test if some condition is actually met?

是的,所有等待接口都受到虚假唤醒的影响,您始终有责任检查自己的谓词。看documentation还是一本好书。

  1. the program currently does not exit ...

pthread_cancel 太可怕了,永远不应该使用。真的很难做到正确。如果你想告诉你的线程退出,写一个通知机制 - 将它构建到现有的谓词循环中 - 和 signal/broadcast 以确保所有线程醒来并意识到它是时候死了。

Regarding exiting deferred: thread_id[0] exits fine and i am stuck in line pthread_join (thread_id[1], NULL);, it says (Exiting) but seems stuck on a lock

关于 pthread_cancel 的困难之一是 清理。如果在您持有锁时取消发生,您需要使用 pthread_cleanup_push 来模拟与取消兼容的 RAII 语义。否则,第一个线程可能(在本例中确实)在互斥量仍处于锁定状态时死掉。

在这种情况下,由于取消,第二个线程试图从 pthread_const_wait 退出,但它需要重新获得锁,但不能。


条件变量循环的通常形式是这样的(一本好的参考书应该有类似的内容):

void *thread(void *data)
{
    struct Args *args = (struct Args *)data;

    /* this lock protects both the exit and work predicates.
     * It should probably be part of your argument struct,
     * globals are not recommended.
     * Error handling omitted for brevity,
     * but you should really check the return values.
     */
    pthread_mutex_lock(&args->mutex);

    while (!exit_predicate(args)) {
        while (!work_predicate(args)) {
            /* check the return value here too */
            pthread_cond_wait(&args->condition, &args->mutex);
        }
        /* work_predicate() is true and we have the lock */
        do_work(args);
    }

    /* unlock (explicitly) only once.
     * If you need to cope with cancellation, you do need
     * pthread_cleanup_push/pop instead.
     */
    pthread_mutex_unlock(&args->mutex);

    return data;
}

您的自定义代码可以直接进入 bool exit_predicate(struct Args*)bool work_predicate(struct Args*)void do_work(struct Args*)。循环结构本身很少需要改动。