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

Имам нужда от просто заключване "един по един" на част от кода. Помислете за функцията 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)


Критичните секции ще работят (те са по-леки от мютексите.) InitializeCriticalSection, EnterCriticalSection, LeaveCriticalSection и DeleteCriticalSection са функциите, които трябва да търсите на MSDN.

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

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

person Michael    schedule 11.03.2009
comment
Досега правилно, но доколкото знам, CriticalSections използват вътрешни мютекси, така че няма полза от производителността. - person Nils Pipenbrinck; 11.03.2009
comment
И след това за безопасност на изключение можете да обвиете cs в клас; виж RAII. - person Reunanen; 11.03.2009
comment
(Тъй като въпросът е за C++.) - person Reunanen; 11.03.2009
comment
Nils - Актуализира отговора с това защо критичните секции са по-бързи. Те не са просто обвивки около мутекси. - 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. използвайки 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();
    }

РЕДАКТИРАНЕ: Вижте статия за 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