CMutex:: Блокировка против CSingleLock::Блокировка

#c #visual-c #mfc #thread-synchronization

#c #visual-c #mfc #синхронизация потоков

Вопрос:

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

 class CClassA : public CObject
{
public:
   void DoSomething();

private:
   CMutex m_mutex;
}

void CClassA::DoSomething()
{
   m_mutex.Lock();

   //...logic...

   m_mutex.Unlock();
}
  

В другом месте того же проекта я обнаружил, что код использует CSingleLock

 class CClassB : public CObject
{
public:
   void DoSomething();

private:
   CCriticalSection m_crit;
}

void CClassB::DoSomething()
{
   CSingleLock lock(amp;m_crit);
   lock.Lock();

   //...logic...

   lock.Unlock();
}
  

После просмотра документации MSDN по синхронизации может показаться, что CClassB реализует рекомендованный метод, но мне не ясно, в чем опасность реализации, используемой CClassA. Насколько я могу судить, единственное различие между двумя методами заключается в том, что CSingleLock имеет преимущество RAII, поэтому блокировка автоматически снимается, когда выполнение выходит из области видимости. Есть ли какие-либо другие преимущества / недостатки в любой реализации?

Комментарии:

1. Чтобы добавить некоторый контекст, одна из моих проблем заключается в том, имеет ли использование CSingleLock::Lock слегка отличающееся поведение. Например, когда один и тот же поток может вызывать CMutex::Lock несколько раз (поскольку он уже владеет блокировкой), вызов CSingleLock::Lock в том же экземпляре CSingleLock будет заблокирован. Я также обеспокоен тем, что могу столкнуться с ситуацией, когда CSingleLock управляет CCriticalSection, но у этого CCriticalSection есть метод разблокировки, вызываемый напрямую.

Ответ №1:

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

Ни один из этих классов по-настоящему не получает преимущества RAII без их переноса, потому что в этом случае вам никогда не понадобилось бы явно вызывать lock или unlock. Возьмем, к примеру, этот небольшой фрагмент псевдокода, использующий блокировку мьютекса boost …

 void DoSomething()
{
  // construction acquires lock on mutex
  boost::scoped_lock lock(amp;aBoostMutex);

  // ...

} // end scope - object is destroyed and lock is released
  

Теперь я бы сказал, что вам следует избегать CMutex , CCritalSection , CSemaphore , и CEvent потому что реализации несколько нарушены или, по крайней мере, уступают другим доступным библиотекам, таким как boost. Например:

  • Определить время ожидания из заброшенного мьютекса невозможно, потому что реализация проверяет только возвращаемое значение, а не причину.
  • Использование реентерабельных блокировок не используется, CSingleLock поэтому рекурсия вызовет проблемы.
  • Невозможность получить именованное событие между процессами

В зависимости от поставленной перед вами задачи у вас может быть возможность отказаться от оболочек MFC в Windows API и либо реализовать свои собственные атомарные блокировки, либо использовать что-то вроде функций boost или C 0x, подобных std::mutex , которые не только являются лучшими реализациями, но и обеспечивают кроссплатформенную поддержку.

Комментарии:

1. К сожалению, я застрял с MFC. Мой вопрос был не столько о разнице между CMutex и CCriticalSection, сколько о разнице между CSyncObject::Lock и CSingleLock::Lock. После проведения дополнительных исследований выяснилось, что CSingleLock ведет себя аналогично boost::scoped_lock, который вы показали выше (вызывает разблокировку при деструкторе). Я не понимал, что CSingleLock предотвращает рекурсию, я предполагал, что каждый новый экземпляр в стеке будет управляться независимо.

2. CSyncObject вызывает Windows api WaitForSingleObject под прикрытием, насколько я знаю, и не может использоваться напрямую. Это интерфейс базового класса, который CMutex является производным от, поэтому он используется, когда вы выполняете CMutex::Lock(); в своем экземпляре. CSingleLock является ли ваш CCriticalSection манипулятор конкретным и может быть использован в качестве блокировки области видимости с некоторым переносом. Как упоминалось ранее, с этими объектами существуют некоторые проблемы с дизайном, поэтому их следует избегать flounder.com/avoid_mfc_syncrhonization.htm

3. Для будущих читателей, CSingleLock имеет параметр конструктора , который приведет к блокировке при построении. В примере OP он не используется, что как бы опровергает основную причину использования CSingleLock.

Ответ №2:

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

Комментарии:

1. Из того, что я могу сказать, этот код никогда не нуждается в синхронизации, которая пересекает границы процесса. Это наводит меня на мысль, что CMutex в CCLassA может быть заменен CCriticalSection.