Использовать мьютекс как семафор?

Мне нужно два потока для продвижения по шаблону "tick tock". При имплантировании семафора это выглядит нормально:

Semaphore tick_sem(1);
Semaphore tock_sem(0);

void ticker( void )
{
   while( true )
   {
      P( tick_sem );
      do_tick();
      V( tock_sem );
   }
}

void tocker( void )
{
   while( true )
   {
      P( tock_sem );
      do_tock();
      V( tick_sem );
   }
}

Однако, если я делаю то же самое с мьютексом (который является технически двоичным семафором), он имеет запах нечетного кода.

std::mutex tick_mutex;
std::mutex tock_mutex;
tock_mutex.lock();

void ticker( void )
{
   while( true )
   {
      tick_mutex.lock();
      do_tick();
      tock_mutex.unlock();
   }
}

void tocker( void )
{
   while( true )
   {
      tock_mutex.lock()
      do_tock();
      tick_mutex.unlock();
   }
}

Я думаю, что запах в том, что мьютекс не предназначен для передачи информации в другой поток. (Стандартный комитет С++ 11 добавил ложную попытку try_lock для предотвращения неожиданной передачи информации, §30.4.1/14.) Кажется, что мьютексы предназначены для синхронизации доступа к переменной, которая затем может передавать информацию в другой поток.

Наконец, при реализации с std::condition_variable он выглядит корректно, но более сложным (переменная tick_vs_tock, мьютекс и переменная условия). Я пропустил реализацию для краткости, но это действительно прямо.

Является ли решение мьютекса прекрасным? Или есть что-то неточно с этим?

Есть ли хороший образец для решения моей проблемы с тиком/табличкой, о которой я не думал?

Ответ 1

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

Вы нарушаете это правило.

Edit:

От MSDN:

Функция ReleaseMutex завершается с ошибкой, если вызывающий поток не имеет объект mutex.

С какого-то сайта, на который google появился pthread_mutex_unlock:

Функция pthread_mutex_unlock() может выйти из строя, если:

EPERM Текущий поток не имеет мьютекса.

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

Ответ 2

Поскольку у вас есть случай использования семафора, я думаю, что исправление заключается в переносимости реализовать один с использованием мьютекса и переменной условия.

Это может быть не особенно эффективно (поскольку он будет использовать пару mutex/condvar для семафора), но вы можете переключать альтернативную реализацию в системах, у которых есть свои семафоры (например, Posix и Windows).

По-видимому семафоры "слишком подвержены ошибкам" . При всем уважении к Boost, я думаю, по крайней мере некоторые из нас могут справиться. Конечно, вы можете связать себя в узлах пытаясь сделать сложные вещи с несколькими семафорами, и это довольно низкоуровневый инструмент. Но когда они правильные, никаких проблем.