Использование форсированных блокировок для доступа RAII к семафору - PullRequest
1 голос
/ 03 мая 2010

Предположим, я пишу класс семафоров C ++ с интерфейсом, который моделирует концепцию повышенной блокировки (т. Е. lock(); unlock(); try_lock(); и т. Д.). Безопасно / рекомендуется ли использовать форсированные блокировки для доступа RAII к такому объекту? Другими словами, предполагают ли повышающие блокировки (и / или другие связанные части библиотеки ускоряющих потоков), что концепция Lockable будет моделироваться только мьютексоподобными объектами, которые заблокированы и разблокированы из того же потока?

Я предполагаю, что можно использовать семафор в качестве модели для Lockable. Я просмотрел некоторые из источников повышения, и это "кажется" хорошо. Блокировки не содержат явных ссылок на this_thread или что-то подобное. Более того, концепция Lockable не имеет функции, подобной whichThreadOwnsMe(). Похоже, я даже смог бы передать ссылку boost::unique_lock<MySemaphore> на boost::condition_variable_any::wait. Тем не менее, документация не совсем ясно о требованиях.

Чтобы проиллюстрировать, что я имею в виду, рассмотрим класс двоичных семафоров «голые кости» по следующим строкам:

class MySemaphore{
  bool locked;
  boost::mutex mx;
  boost::condition_variable cv;
public:
  void lock(){
    boost::unique_lock<boost::mutex> lck(mx);
    while(locked) cv.wait(lck);
    locked=true;
  }

  void unlock(){
    {
      boost::lock_guard<boost::mutex> lck(mx);
      if(!locked) error();
      locked=false;
    }
    cv.notify_one();
  }
// bool try_lock(); void error(); etc.
}

Теперь предположим, что где-то, на объекте или глобально, у меня есть

MySemaphore sem;

Я хочу заблокировать и разблокировать его с помощью RAII. Также я хочу иметь возможность «передать» владение блокировкой из одного потока в другой. Например, в одном потоке я выполняю

void doTask()
{
  boost::unique_lock<MySemaphore> lock(sem);
  doSomeWorkWithSharedObject();
  signalToSecondThread();
  waitForSignalAck();
  lock.release();
}

Пока другой поток выполняет что-то вроде

{
waitForSignalFromFirstThread();
ackSignal();
boost::unique_lock<MySemaphore>(sem,boost::adopt_lock_t());
doMoreWorkWithSameSharedObject();
}

Причина, по которой я это делаю, заключается в том, что я не хочу, чтобы кто-либо еще мог установить блокировку на sem между временем выполнения первого потока doSomeWorkWithSharedObject() и временем выполнения второго doMoreWorkWithSameSharedObject() , По сути, я делю одну задачу на две части. И причина, по которой я делю задачу, состоит в том, что (1) я хочу, чтобы первая часть задачи началась как можно скорее, (2) я хочу гарантировать, что первая часть завершена до того, как doTask () вернется, и (3) я хочу, чтобы вторая, более трудоемкая часть задачи была выполнена другим потоком, возможно, выбранным из пула подчиненных потоков, которые ожидают завершения задач, которые были запущены мастер-потоками.

ПРИМЕЧАНИЕ: я недавно опубликовал этот же вопрос (вроде) здесь Повышение моделирования :: Блокируется семафором, а не мьютексом (ранее назывался: разблокировка мьютекса из другого потока) но я перепутал мьютексы с семафорами, и поэтому вопрос об использовании буст-блокировок действительно не был решен.

1 Ответ

1 голос
/ 03 мая 2010

@ Дэн, я думаю, вы слишком усложняете вещи. То, что вы описываете, легко достижимо с помощью основного потока обработки, синхронизированной очереди и [пула] рабочих потоков. Кроме того, похоже, что вы попали в общую ловушку использования блокировок для «защиты кода», тогда как вам необходимо защищать структуры данных.

Определите ваши общие данные, определите минимальные критические разделы, когда данные могут быть противоречивыми. Подтяжка та с замками.

...