这个生产者/消费者实施有什么问题?

时间:2010-07-19 01:56:50

标签: c++ pthreads

所以我正在考虑在C ++中使用一个简单的生产者/消费者队列。我最终会使用boost进行线程化,但这个例子只是使用pthreads。我最终还会使用更多的OO方法,但我认为这会掩盖我目前感兴趣的细节。

无论如何,我担心的特殊问题是

  1. 由于此代码使用的是st_ :: deque的push_back和pop_front - 它可能在不同的线程中对底层数据进行分配和释放 - 我认为这很糟糕(未定义的行为) - 避免这种情况最简单的方法是什么? / LI>
  2. 没有任何东西标记为不稳定。但重要的部分是互斥保护。我是否需要将任何东西标记为易变的,如果是这样的话? - 我不认为我这样做,因为我认为互斥锁包含适当的内存屏障等,但我不确定。
  3. 还有其他明显的问题吗?

    无论如何都是代码:

    #include <pthread.h>
    #include <deque>
    #include <iostream>
    
    struct Data
    {
      std::deque<int> * q;
      pthread_mutex_t * mutex;
    };
    
    void* producer( void* arg )
    {
      std::deque<int> &q = *(static_cast<Data*>(arg)->q);
      pthread_mutex_t * m =  (static_cast<Data*>(arg)->mutex);
    
      for(unsigned int i=0; i<100; ++i)
      {
        pthread_mutex_lock( m );
        q.push_back( i );
        std::cout<<"Producing "<<i<<std::endl;
        pthread_mutex_unlock( m );
      }
      return NULL;
    }
    
    void* consumer( void * arg )
    {
      std::deque<int> &q = *(static_cast<Data*>(arg)->q);
      pthread_mutex_t * m =  (static_cast<Data*>(arg)->mutex);
    
      for(unsigned int i=0; i<100; ++i)
      {
        pthread_mutex_lock( m );
        int v = q.front();
        q.pop_front();
        std::cout<<"Consuming "<<v<<std::endl;
        pthread_mutex_unlock( m );
      }  
      return NULL;
    }
    
    int main()
    {
      Data d;
    
      std::deque<int> q;
      d.q = &q;
    
      pthread_mutex_t mutex;
      pthread_mutex_init( &mutex, NULL );
      d.mutex = & mutex;
    
      pthread_t producer_thread;
      pthread_t consumer_thread;
    
      pthread_create( &producer_thread, NULL, producer, &d );
      pthread_create( &consumer_thread, NULL, consumer, &d );
    
      pthread_join( producer_thread, NULL );
      pthread_join( consumer_thread, NULL );
    }
    

    修改

    我最终放弃了这个实现,我现在正在使用Anthony Williams的here代码的修改版本。可以找到我的修改版本here此修改版本使用更合理的基于条件变量的方法。

2 个答案:

答案 0 :(得分:2)

  

由于此代码使用的是st_ :: deque的push_back和pop_front - 它可能在不同的线程中对底层数据进行分配和释放 - 我认为这很糟糕(未定义的行为) - 避免这种情况最简单的方法是什么? / p>

只要一个线程一次只能修改容器,这没关系。

  

没有任何东西标记为不稳定。但重要的部分是互斥保护。我是否需要将任何东西标记为易变的,如果是这样的话? - 我不认为我这样做,因为我认为互斥锁包含适当的内存屏障等,但我不确定。

只要您使用互斥锁正确控制对容器的访问,它就不需要volatile(这取决于您的线程库,但如果它没有,它将不是一个非常好的互斥锁不提供正确的记忆障碍。

答案 1 :(得分:1)

  1. 如果两个线程都在同一个进程中,那么在一个线程中分配内存并在另一个线程中释放它是完全有效的。

  2. 使用互斥锁保护对双端队列的访问应提供正确的内存访问配置。

  3. 编辑:唯一需要考虑的是生产者和消费者的性质。您合成的示例缺少真正实现所涉及的一些细微之处。例如,如果生产者没有以完全相同的速率运营,您将如何将生产者与消费者同步?您可能需要考虑使用管道或OS队列而不是双端队列,以便在没有数据准备好处理时,消费者可以阻止读取。