Простая синхронизация потоков

Мне нужна простая блокировка «по одному» в разделе кода. Рассмотрим функцию func, которую можно запускать из нескольких потоков:

void func()
{
     // locking/mutex statement goes here
     operation1();
     operation2();
     // corresponding unlock goes here
     operation3();
}

Мне нужно убедиться, что operation1 и operation2 всегда работают "вместе". В C# я бы использовал простой блок lock вокруг этих двух вызовов. Что такое эквивалент C++/Win32/MFC?

Предположительно какой-то Mutex?


person Nick    schedule 11.03.2009    source источник


Ответы (5)


Критические секции будут работать (они легче, чем мьютексы). /windows/desktop/ms686908(v=vs.85).aspx" rel="nofollow noreferrer" title="MSDN">MSDN.

void func()
{
    // cs previously initialized via InitializeCriticalSection
    EnterCriticalSection(&cs);
    operation1();
    operation2();
    LeaveCriticalSection(&cs);
    operation3();}
}

РЕДАКТИРОВАТЬ: Критические секции быстрее, чем мьютексы, поскольку критические секции в основном являются примитивами пользовательского режима - в случае неконкурентного захвата (обычно распространенный случай) в ядро ​​​​не выполняется системный вызов, а получение занимает порядка десятков циклов. Переключатель ядра дороже (порядка сотен циклов). Единственные критические по времени секции вызывают ядро ​​​​для блокировки, что включает ожидание примитива ядра (либо мьютекс, либо событие). Получение мьютекса всегда связано с обращением к ядру и, таким образом, на несколько порядков медленнее. Однако критические секции можно использовать только для синхронизации ресурсов в одном процессе. Для синхронизации между несколькими процессами необходим мьютекс.

person Michael    schedule 11.03.2009
comment
Пока правильно, но, насколько я знаю, CriticalSections используют мьютексы внутри, так что выигрыша в производительности нет. - person Nils Pipenbrinck; 11.03.2009
comment
И тогда для безопасности исключений вы можете обернуть cs в класс; см. РАИИ. - person Reunanen; 11.03.2009
comment
(Поскольку вопрос касается С++.) - person Reunanen; 11.03.2009
comment
Нильс - Обновлен ответ, почему критические разделы работают быстрее. Это не просто обертки вокруг мьютексов. - person Michael; 11.03.2009
comment
Критическая секция действительно намного быстрее, чем мьютексы. Даже безымянные мьютексы. - person John Dibling; 11.03.2009
comment
Вероятно, вам следует обернуть это в объект. Использование такого кода C НЕ является безопасным для исключений. - person Martin York; 11.03.2009
comment
Для поддержки слов Майкла здесь приведена документация MSDN msdn.microsoft.com/en -us/library/h5zew56b.aspx#Anchor_2, где они также заявили, что критические секции используются вместо мьютексов (см. CMutex), когда критична скорость и ресурс не будет использоваться через границы процесса. - person Mak; 28.03.2017

Исправление решения Майкла выше.

Решение Michael идеально подходит для приложений C. Но при использовании в C++ этот стиль не рекомендуется из-за возможности исключений. Если в операции1 или операции2 произойдет исключение, критическая секция не будет правильно оставлена, и все остальные потоки будут заблокированы в ожидании.

// Perfect solutiuon for C applications
void func()
{
    // cs previously initialized via InitializeCriticalSection
    EnterCriticalSection(&cs);
    operation1();
    operation2();
    LeaveCriticalSection(&cs);
    operation3();}
}

// A better solution for C++
class Locker
{
    public:
    Locker(CSType& cs): m_cs(cs)
    {
        EnterCriticalSection(&m_cs);
    }
    ~Locker()
    {
        LeaveCriticalSection(&m_cs);
    }
    private:
        CSType&  m_cs;
}
void func()
{
    // cs previously initialized via InitializeCriticalSection
    {
        Locker  lock(cs);
        operation1();
        operation2();
    }
    operation3();
}
person Martin York    schedule 11.03.2009
comment
Искусство программирования. Очень элегантный. Легче различать блоки критических разделов и меньше вероятность ошибок. :-) - person StanE; 27.05.2016
comment
Лучшие ответы когда-либо. :) - person Mak; 28.03.2017

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

person Murray    schedule 11.03.2009

  1. с помощью МФЦ:

    1. Определите объект синхронизации. (Мутекст или критический раздел)

      1.1 Если в функцию func() входит несколько потоков, принадлежащих разным процессам, используйте CMutex.

      1.2. Если несколько потоков одного и того же процесса входят в func(), используйте CCriticalSection.

    2. CSingleLock можно использовать для упрощения использования объектов синхронизации.

Допустим, мы определили критическую секцию

 CCriticalSection m_CriticalSection;
    void func()
    {
         // locking/mutex statement goes here
         CSingleLock aLock(&m_CriticalSection, **TRUE**); 
       // TRUE indicates that Lock aquired during aLock creation.
       // if FALSE used then use aLock.Lock() for locking.

         operation1();
         operation2();
          // corresponding unlock goes here
          aLock.Unlock();
         operation3();
    }

РЕДАКТИРОВАТЬ: см. статью VC++ из MSDN: Многопоточность с классами C++ и MFC и Многопоточность: как использовать классы синхронизации< /а>

person aJ.    schedule 11.03.2009

Вы можете попробовать это:

void func()
{
    // See answer by Sasha on how to create the mutex
    WaitForSingleObject (mutex, INFINITE);
    operation1();
    operation2();
    ReleaseMutex(mutex);
    operation3();
}
person Dani van der Meer    schedule 11.03.2009
comment
WaitForSingleObject работает медленнее. CriticalSections может принимать счетчик оборотов и будет вращаться некоторое время, прежде чем перейти к WaitForSingleObject. см. msdn.microsoft.com/en-us/library/ ms683476(VS.85).aspx Функция InitializeCriticalSectionAndSpinCount - person Andy Dent; 12.08.2010