2009-03-11 2 views
6

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

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

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

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

ответ

19

Критические разделы будут работать (они имеют меньший вес, которые мьютексы.) InitializeCriticalSection, EnterCriticalSection, LeaveCriticalSection и DeleteCriticalSection - это функции, которые нужно искать в MSDN.

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

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

+0

Исправить пока, но насколько я знаю, CriticalSections использует мьютексы внутри, поэтому выгоды от производительности не существует. –

+0

И тогда для безопасности исключений вы можете перенести cs в класс; см. RAII. – Reunanen

+0

(Так как вопрос о C++.) – Reunanen

2

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

void func() 
{ 
    // See answer by Sasha on how to create the mutex 
    WaitForSingleObject (mutex, INFINITE); 
    operation1(); 
    operation2(); 
    ReleaseMutex(mutex); 
    operation3(); 
} 
+0

WaitForSingleObject работает медленнее. CriticalSections может принимать число оборотов и некоторое время вращаться, прежде чем провалиться в WaitForSingleObject. см. http://msdn.microsoft.com/en-us/library/ms683476(VS.85).aspx InitializeCriticalSectionAndSpinCount Функция –

6

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

6
  1. с помощью MFC:

    1. Определить объект синхронизации. (Mutext или Critical section)

      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(); 
    } 

EDIT: См VC++ статьи из MSDN: Multithreading with C++ and MFC Classes и Multithreading: How to Use the Synchronization Classes

25

Крепление Michael solution выше.

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

// 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(); 
} 
+0

+1 Лучшая практика RAII .. – deepdive

+0

Искусство программирования. Очень элегантно. Легче различать блоки критических секций и с меньшей вероятностью совершать ошибки. :-) – StanE

+0

Лучшие ответы всегда. :) – Mak

Смежные вопросы