другой мьютекс для push и pop - PullRequest
       17

другой мьютекс для push и pop

1 голос
/ 08 апреля 2011

У меня есть класс с именем'метарная подписка '. Этот класс получает свои данные через подписанные издатели (множественное число), вызывающие его метод push.

В другом потоке метод pop этого класса вызывается для получения этих данных. Таким образом, в некотором смысле этот класс является своего рода буфером между несколькими издателями и их подписчиками. Для реализации я основывался на информации, найденной о поточно-ориентированных очередях здесь .

Теперь мой вопрос двоякий:

  • Если бы я использовал один и тот же мьютекс для ввода и вывода значений (в настоящее время я использую два разных мьютекса), возможно ли, что моя программа застрянет, ожидая заблокированного нажатия?
  • Если нет, то как методы push и pop могут преодолеть блокировку (the_same_mutex).

Я предполагаю, что, если бы я использовал тот же мьютекс и программа вошла в метод pop, она получила бы блокировку pop, проверила, пуста ли очередь, и подождала переменную условия, которая никогда не может быть установлена ​​в метод push (так как блокировка уже получена pop).

текущий код (с использованием двух разных мьютексов):

#include <boost/thread.hpp>
#include <queue>
#include "subscriber.h"
#include "pubdata.h"
#ifdef DEBUG
#include <iostream>
#include <boost/lexical_cast.hpp>
#endif

namespace PUBLISHSUBSCRIBE
{
  template<class T>
  class SubscribedQueue: public PUBLISHSUBSCRIBE::Subscriber<T>, private std::queue< PubData<T> >
  {
  public:
    PubData<T>  pop();   //removes the next item from the queue, blocks until the queue is not empty
    void push(const PubData<T> data); //method used by the publisher to push data onto the queue
  private:
    mutable boost::mutex writeMutex_; //only needed for publishing/pushing data
    mutable boost::mutex readMutex_;  //only needed for reading/popping data
    boost::condition_variable notify_;
  };

  template<class T>
  PubData<T> SubscribedQueue<T>::pop() { //Blocks until the queue is not empty
    boost::mutex::scoped_lock lock(readMutex_);
    while(std::queue< PubData<T> >::empty())
      notify_.wait(lock); //block until recieving a notification AND the queue is not empty
    PubData<T> head = std::queue< PubData<T> >::front();
    std::queue< PubData<T> >::pop();
#ifdef DEBUG
    std::string debugOut("pop: " + boost::lexical_cast<std::string>(head) + " - timestamp: " + boost::lexical_cast<std::string>(head.timestamp()) + " - from: " + boost::lexical_cast<std::string>(this) + "\n" );
    std::cout <<debugOut;
#endif
    lock.unlock();
    return head;
  }

  template<class T>
  void SubscribedQueue<T>::push(const PubData<T> data){
    boost::mutex::scoped_lock lock(writeMutex_);
#ifdef DEBUG
    std::cout << "published: " << data << std::endl;
#endif
    std::queue< PubData<T> >::push(data);
    lock.unlock();
    notify_.notify_one();
  }
}
#endif //SUBSCRIBEDQUEUE_H

[править] Что меня больше всего беспокоит, так это то, что у меня есть boost :: condition_variable notify_, для которого в pop-режиме выполняется ожидание уведомления. Но pop должен сначала заблокировать мьютекс, тот же мьютекс, который также должен быть заблокирован в «push», чтобы «уведомить» условную переменную.

Так не приведет ли это к тупику, а почему бы и нет?

Ответы [ 5 ]

3 голосов
/ 08 апреля 2011

Контейнеры стандартной библиотеки не являются поточно-ориентированными;Если вы попытаетесь изменить контейнер из нескольких потоков одновременно, то произойдут плохие вещи .

Если у вас есть отдельный мьютекс для операций push и pop, то вы не защитите от одновременного нажатия иpop из двух потоков, так что вы на самом деле не защищали коллекцию вообще.

Я предполагаю, что если бы я использовал тот же мьютекс и программа вошла в метод pop, она получила бызаблокируйте pop, проверьте, пуста ли очередь, и дождитесь условной переменной, которую никогда нельзя установить в методе push (так как блокировка уже получена pop).

Когда вы ожидаете наусловная переменная в pop, wait () разблокирует мьютекс, поэтому push () во время ожидания сможет заблокировать его.push () вызывает notify_one () и разблокирует мьютекс благодаря тому, что scoped_lock выходит из области видимости в конце функции.Затем, когда поток pop () будет в следующий раз запланирован, он немедленно повторно заблокирует мьютекс и продолжит работу.

1 голос
/ 08 апреля 2011

Да, вы должны использовать один мьютекс, в противном случае говорят, что push освободил место для значения и увеличил размер, но не завершил копирование этого значения на место ... читатель мог прочитать любой тип мусора.

Вам не нужно беспокоиться о взаимоблокировке читателей и писателей - переменная условия предназначена для разрешения этой ситуации, позволяя потоку подталкивания работать, пока поток оповещения ожидает уведомления.

1 голос
/ 08 апреля 2011

Во-первых, вам не нужно иметь мьютексы mutable, поскольку ваш код стоит сейчас - они, похоже, не используются ни в одной const функции.

Во-вторых, вместо двухмьютексы, один для чтения и один для записи, у вас должен быть только один мьютекс для доступа к очереди и ограничение этого доступа к самой низкой области видимости, удерживая блокировку не как инструкцию дольше, чем необходимо.

1 голос
/ 08 апреля 2011

По определению "pop" - это акт удаления элемента из списка.Поэтому, если вы хотите выдвигать и извлекать из одного и того же списка несколько потоков, вам нужно использовать один и тот же мьютекс для защиты этого списка.

Если вы используете разные мьютексы, это означает, что эти разные потоки могут одновременно добавлять / удалять элементы и, таким образом, портить список.

Когда поток А добавляет в список, защищенныймьютекс, а затем поток B, пытающийся удалить элемент из этого списка, должен будет ждать, пока поток A завершит добавление элемента и покинет блокировку.

0 голосов
/ 08 апреля 2011

Вы должны использовать только один Mutex, чтобы создать потокобезопасную очередь, потому что вы должны гарантировать, что только один поток одновременно обращается к очереди (независимо от того, является ли он push или pop).

Toответьте на свои вопросы: 1) В вашей текущей реализации push и pop нет возможности тупика (если вы не используете свой мьютекс в другом месте), потому что блокировка ограничена областью действия push и pop, и scoped_lock освобождает мьютекс также вслучай исключения.2) Push и pop могут преодолеть блокировку тогда и только тогда, когда мьютекс в данный момент не заблокирован.В противном случае поток будет приостановлен до тех пор, пока блокирующий поток не освободит мьютекс.

...