Каковы проблемы с этой реализацией производителя / потребителя? - PullRequest
4 голосов
/ 19 июля 2010

Итак, я смотрю на использование простой очереди производителя / потребителя в C ++.В итоге я буду использовать boost для многопоточности, но этот пример просто использует pthreads.Я также в конечном итоге использую гораздо более ОО подход, но я думаю, что это затенило бы детали, которые меня интересуют в данный момент.

В любом случае, особые проблемы, о которых я беспокоюсь, это

  1. Поскольку этот код использует push_back и pop_front из std :: deque - он, вероятно, выполняет распределение и освобождение базовых данных в разных потоках - я считаю, что это плохо (неопределенное поведение) - какой самый простой способ избежать этого?
  2. Ничто не помечено как изменчивое.Но важные биты защищены мьютексом.Нужно ли помечать что-либо как изменчивое и если да, то что?- Я не думаю, что делаю, поскольку считаю, что мьютекс содержит соответствующие барьеры памяти и т. Д., Но я не уверен.

Есть ли какие-либо другие вопиющие проблемы?код:

#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 );
}

РЕДАКТИРОВАТЬ:

Я закончил тем, что выбросил эту реализацию, я сейчас использую модифицированную версию кода из здесь Энтони Уильямс.Мою модифицированную версию можно найти здесь Эта модифицированная версия использует более разумный подход, основанный на переменных условий.

Ответы [ 2 ]

2 голосов
/ 19 июля 2010

Поскольку этот код использует push_back и pop_front из std :: deque - он, вероятно, выполняет распределение и освобождение лежащих в основе данных в разных потоках - я считаю, что это плохо (неопределенное поведение) - какой самый простой способ избежать этого?

Пока контейнер может изменять только один поток, это нормально.

Ничто не помечено как изменчивое. Но важные биты защищены мьютексом. Нужно ли помечать что-либо как изменчивое и если да, то что? - Я не думаю, что я делаю, поскольку я считаю, что мьютекс содержит соответствующие барьеры памяти и т. Д., Но я не уверен.

Пока вы правильно управляете доступом к контейнеру с помощью мьютекса, он не обязательно должен быть volatile (это зависит от вашей библиотеки потоков, но это не будет очень хорошим мьютексом, если он этого не сделал обеспечить правильный барьер памяти).

1 голос
/ 19 июля 2010
  1. Совершенно верно выделить память в одном потоке и освободить ее в другом, если оба потока находятся в одном процессе.

  2. Использование мьютекса для защитыдоступ к деку должен обеспечивать правильную конфигурацию доступа к памяти.

РЕДАКТИРОВАТЬ: Единственное, о чем нужно думать, это о природе производителя и потребителя.В вашем синтезированном примере отсутствуют некоторые тонкости, связанные с реальной реализацией.Например, как вы будете синхронизировать производителя с потребителем, если они не работают с одинаковой скоростью?Возможно, вы захотите использовать что-то вроде конвейера или очереди ОС вместо deque, чтобы потребитель мог заблокировать чтение, если нет данных, готовых для обработки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...