tags:

views:

692

answers:

5

I need a simple "one at a time" lock on a section of code. Consider the function func which can be run from multiple threads:

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

I need to make sure that operation1 and operation2 always run "together". With C# I would use a simple lock block around these two calls. What is the C++/Win32/MFC equivalent?

Presumably some sort of Mutex?

+13  A: 

Critical sections will work (they're lighter-weight that mutexes.) InitializeCriticalSection, EnterCriticalSection, LeaveCriticalSection, and DeleteCriticalSection are the functions to look for on MSDN.

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

EDIT: Critical sections are faster than mutexes since critical sections are primarily user mode primitives - in the case of an uncontended acquire (usually the common case) there is no system call into the kernel, and acquiring takes on the order of dozens of cycles. A kernel switch is more more expensive (on the order of hundreds of cycles). The only time critical sections call into the kernel is in order to block, which involves waiting on a kernel primitive, (either mutex or event). Acquiring a mutex always involves a call into the kernel, and is thus orders of magnitude slower. However, critical sections can only be used to synchronize resources in one process. In order to synchronize across multiple processes, a mutex is needed.

Michael
Correct so far, but as far as I know CriticalSections use mutexes internally, so there is not performance benefit.
Nils Pipenbrinck
And then for exception safety you could wrap cs into a class; see RAII.
Pukku
(Since the question is about C++.)
Pukku
Nils - Updated the answer with why critical sections are faster. They're not just wrappers around mutexes.
Michael
Critical section are, indeed, much faster than mutexes. Even unnamed mutexes.
John Dibling
You should probably wrap that in an object. Using C code like that is NOT exception safe.
Martin York
+1  A: 

You can try this:

void func()
{
    // See answer by Sasha on how to create the mutex
    WaitForSingleObject (mutex, INFINITE);
    operation1();
    operation2();
    ReleaseMutex(mutex);
    operation3();
}
Dani van der Meer
WaitForSingleObject is slower. CriticalSections can take a spin count and will spin for a while before falling through to WaitForSingleObject. see http://msdn.microsoft.com/en-us/library/ms683476(VS.85).aspx InitializeCriticalSectionAndSpinCount Function
Andy Dent
+5  A: 

The best method would be to use a critical section, use EnterCriticalSection and LeaveCriticalSection. The only ticky part is that you need to initialize a critical section first with InitializeCriticalSection. If this code is within a class, put the initialization in the constructor and the CRITICAL_SECTION data structure as a member of the class. If the code is not part of a class, you need to likely use a global or something similiar to ensure it is initialized once.

Murray
+5  A: 
  1. using MFC:

    1. Define a synchronization object. ( Mutext or Critical section)

      1.1 If multiple threads belonging to different process enters the func() then use CMutex.

      1.2. If multiple threads of same process enters the func() then use CCriticalSection.

    2. CSingleLock can be used to ease the usage of synchronization objects.

Lets say we have defined critical section

 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: Refer VC++ article from MSDN: Multithreading with C++ and MFC Classes and Multithreading: How to Use the Synchronization Classes

aJ
+10  A: 

Fixing Michael solution above.

Michael solution is perfect for C applications. But when used in C++ this style is discouraged because of the posability of exceptions. If an exception happens in operation1 or operation2 then the critical section will not be correctly left and all other threads will block waiting.

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