简体   繁体   English

生产者-消费者问题:使用条件变量时,posix互斥锁被锁定两次?

[英]producer-consumer problem:posix mutex got locked twice when using condition variable?

Following code is only to show how to use condition variable to synchronize threads(one producer and many consumers) as exercising. 以下代码仅显示如何在执行时使用条件变量来同步线程(一个生产者和许多消费者)。 See the line for code 'usleep(100);'.When I comment this line,two consumer threads seem got the mutex locked at the same time after the producer thread exit,and then wait on 'cond',this raise race condition. 请参见代码行'usleep(100);'。当我对此行进行注释时,两个使用者线程似乎在生产者线程退出后同时锁定了互斥锁,然后等待“ cond”,这提高了竞争条件。 But if I uncomment it.everything goes well(seems). 但是,如果我不加评论,一切都会好起来(似乎)。

My question is how can two consumer threads lock one mutex at the same time?This demo should also work even after I don't call usleep()?Thanks for your time in advance. 我的问题是两个使用者线程如何才能同时锁定一个互斥锁?即使我没有调用usleep(),此演示也应该工作?谢谢您的时间。

This is the output after usleep removed from loops in producer.Please note the last two 'lock' in the output. 这是usleep从生产者循环中删除后的输出。请注意输出中的最后两个“锁”。

$ ./pthread_cond 
Producer 3062414192 beginning...
Producer locked mutex self:3062414192
Producer is creating work:1
Producer finished creating work:1
Producer unlock self:3062414192
Producer locked mutex self:3062414192
Producer is creating work:2
Producer finished creating work:2
Producer unlock self:3062414192
Producer locked mutex self:3062414192
Producer is creating work:3
Producer finished creating work:3
Producer unlock self:3062414192
Producer locked mutex self:3062414192
Producer is creating work:4
Producer finished creating work:4
Producer unlock self:3062414192
Producer 3062414192 exit after creating 4 works...
produce joined,but 4 work remained
Consumer 3070806896 beginning...
Consumer locked mutex self:3070806896
to wait on cond self:3070806896
Consumer 3079199600 beginning...
Consumer locked mutex self:3079199600
to wait on cond self:3079199600

Implemented: 实施:

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#define MAX_COUSUMER 2

#define TOTAL_WORK 4

int g_work_counter=0;

pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond=PTHREAD_COND_INITIALIZER;

void *producer_thread(void* arg)
{
    int i;

    printf("Producer %lu beginning...\n",pthread_self());
    for(i=0;i<TOTAL_WORK;i++)
    {
        assert(pthread_mutex_lock(&mut)==0);
        printf("Producer locked mutex self:%lu\n",pthread_self());
        printf("Producer is creating work:%d\n",g_work_counter+1);
        g_work_counter++;
        printf("Producer finished creating work:%d\n",g_work_counter);
        pthread_cond_broadcast(&cond);
        assert(pthread_mutex_unlock(&mut)==0);
        printf("Producer unlock self:%lu\n",pthread_self());

        //usleep(100);
    }

    printf("Producer self:%lu exit after creating %d works...\n",pthread_self(),i);//counter starts from 0
    pthread_exit(NULL);
}

void *consumer_thread(void *arg)
{
    printf("Consumer %lu beginning...\n",pthread_self());
    //use pthread_cancel in main
    pthread_detach(pthread_self());

    while(1)
    {
        assert(pthread_mutex_lock(&mut)==0);
        printf("Consumer locked mutex self:%lu\n",pthread_self());
        printf("to wait on cond self:%lu\n",pthread_self());
        assert(pthread_cond_wait(&cond,&mut)==0);
        if(g_work_counter)
        {
            printf("Consumer %lu is performing work:%d\n",pthread_self(),g_work_counter);
            g_work_counter--;
            printf("Consumer %lu finished performing work:%d\n",pthread_self(),g_work_counter+1);
        }
        assert(pthread_mutex_unlock(&mut)==0);
        printf("Consumer unlock self:%lu\n",pthread_self());
    }

    //no output (pthread_cancel is called)
    printf("Consumer %lu exit...\n",pthread_self());
    pthread_exit(NULL);
}

int main(int argc,char* argv[])
{
    pthread_t producer;
    pthread_t consumers[MAX_COUSUMER];
    int i;

    for(i=0;i<MAX_COUSUMER;i++)
    {
        if(pthread_create(&consumers[i],NULL,consumer_thread,NULL)!=0)
        {
            printf("pthread_create failed for consumer_thread %d\n",i);
        }
    }

    pthread_create(&producer,NULL,producer_thread,NULL);

    if(pthread_join(producer,NULL)!=0)
    {
        printf("pthread_join failed for producer_thread %lu\n",consumers[i]);
    }
    printf("producer joined,but %d work remained\n",g_work_counter);

    //wait for the consumers
    while(g_work_counter>0)
        ;

    //cancel the consumer,for they are detached
    for(i=0;i<MAX_COUSUMER;i++)
    {
        if(pthread_cancel(consumers[i])!=0)
        {
            printf("pthread_cancel failed for consumer_thread %d\n",i);
        }
    }

    pthread_mutex_destroy(&mut);
    pthread_cond_destroy(&cond);
    return 0;
}

When a thread waits on a condition, it releases the lock. 当线程等待条件时,它将释放锁定。 When it is woken up, it reacquires it. 唤醒后,它将重新获取它。 In this code, a consumer should only wait if the buffer is empty. 在此代码中,使用者仅应在缓冲区为空时等待。

Another problem lies in main , actually, with this line: while(g_work_counter>0) . 另一个问题实际上是在main这一行: while(g_work_counter>0) You don't have the lock at that point, so it's not safe to check g_work_counter . 此时您没有锁,因此检查g_work_counter并不安全。 I'm also not too sure about pthread_detach(pthread_self()); 我也不太清楚pthread_detach(pthread_self()); . Shouldn't that be called by main on its own child? 那不应该由main自己的孩子来调用吗?

As a general note, if you want to check for deadlocks and otherwise debug your pthreads code, you should use the pthread_mutexattr_foo functions to set up an error-checking mutex, and check the return value with more than just an assert(==0) . 作为一般说明,如果要检查死锁并以其他方式调试pthreads代码,则应使用pthread_mutexattr_foo函数来设置错误检查互斥体,并使用不仅仅是assert(==0)检查返回值。 。

As Borealid says, the lock is released while the thread is waiting on the condition variable. Borealid所说,当线程等待条件变量时,将释放锁定。 Your consumer function should look like this: 您的使用者函数应如下所示:

    /* Wait for some work to be available */
    pthread_mutex_lock(&mut);
    while (g_work_counter == 0)
    {
        pthread_cond_wait(&cond, &mut);
    }

    /* Have lock, g_work_counter is > 0, so do work */
    while (g_work_counter > 0)
    {
        g_work_counter--;
    }

    pthread_mutex_unlock(&mut);

( pthread_cond_wait() should always be used inside a spinning while loop like that). pthread_cond_wait()应该始终在类似的while循环中使用)。

I would propose that you stay clear of pthread_cancel() API. 我建议您不要使用pthread_cancel()API。 Its very hard to use pthread_cancel() without introducing resource leaks, deadlocks and inconsistencies in your program. 在不引入资源泄漏,死锁和程序不一致的情况下,很难使用pthread_cancel()。

As a rule of thump: Use detached threads for "one-shot/i don't care about the results/the completion of something" kind of things. 作为重头戏:将分离线程用于“一次/我不在乎结果/完成某件事”之类的事情。 For other things use "normal threads" which shut down in a cooperative way (by checking a flag). 对于其他事情,请使用以协作方式(通过检查标志)关闭的“正常线程”。

So in your example you want all the work to be consumed before exiting the main program. 因此,在您的示例中,您希望在退出主程序之前消耗所有工作。 Don't create the consumers as detached and replace the loop which canceled all the consumers with a loop doing a pthread_join() for all consumers. 不要将消费者创建为分离的,而要用对所有消费者执行pthread_join()的循环来替换取消所有消费者的循环。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM