Click here to Skip to main content
15,880,956 members
Please Sign up or sign in to vote.
1.44/5 (2 votes)
See more:
Hi, how to implement/use a mutex in C++? Any sample code or tutorial?
Preferably without using MFC or Boost libraries.
Thank you.
Posted
Updated 17-Sep-19 0:36am
v3
Comments
Matthew Faithfull 22-Mar-13 17:11pm    
Are you really interested in Implement or in Use? Those are very different things.
Use is hard but there are lots of examples and Win32 Mutex API is reasonable.
Implement is HARD^3 and samples that don't involve Boost and won't lead you to the dark side are very few.
Volynsky Alex 22-Mar-13 17:27pm    
See here:
Creating and Using Mutex Objects : http://www.codeproject.com/Tips/332431/Creating-and-Using-Mutex-Objects

Mutex Objects:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms684266%28v=vs.85%29.aspx

Hi Matra,

You might want to use the Win32 Mutex object.

This article on MSDN should get you on the way:

Using Mutex Objects[^]
 
Share this answer
 
Comments
[no name] 22-Mar-13 17:16pm    
thank you.
Example ( from http://msdn.microsoft.com/en-us/library/windows/desktop/ms686927%28v=vs.85%29.aspx[^] ) :
C++
#include <windows.h>
#include <stdio.h>

#define THREADCOUNT 2

HANDLE ghMutex; 

DWORD WINAPI WriteToDatabase( LPVOID );

int main( void )
{
    HANDLE aThread[THREADCOUNT];
    DWORD ThreadID;
    int i;

    // Create a mutex with no initial owner

    ghMutex = CreateMutex( 
        NULL,              // default security attributes
        FALSE,             // initially not owned
        NULL);             // unnamed mutex

    if (ghMutex == NULL) 
    {
        printf("CreateMutex error: %d\n", GetLastError());
        return 1;
    }

    // Create worker threads

    for( i=0; i < THREADCOUNT; i++ )
    {
        aThread[i] = CreateThread( 
                     NULL,       // default security attributes
                     0,          // default stack size
                     (LPTHREAD_START_ROUTINE) WriteToDatabase, 
                     NULL,       // no thread function arguments
                     0,          // default creation flags
                     &ThreadID); // receive thread identifier

        if( aThread[i] == NULL )
        {
            printf("CreateThread error: %d\n", GetLastError());
            return 1;
        }
    }

    // Wait for all threads to terminate

    WaitForMultipleObjects(THREADCOUNT, aThread, TRUE, INFINITE);

    // Close thread and mutex handles

    for( i=0; i < THREADCOUNT; i++ )
        CloseHandle(aThread[i]);

    CloseHandle(ghMutex);

    return 0;
}

DWORD WINAPI WriteToDatabase( LPVOID lpParam )
{ 
    // lpParam not used in this example
    UNREFERENCED_PARAMETER(lpParam);

    DWORD dwCount=0, dwWaitResult; 

    // Request ownership of mutex.

    while( dwCount < 20 )
    { 
        dwWaitResult = WaitForSingleObject( 
            ghMutex,    // handle to mutex
            INFINITE);  // no time-out interval
 
        switch (dwWaitResult) 
        {
            // The thread got ownership of the mutex
            case WAIT_OBJECT_0: 
                __try { 
                    // TODO: Write to the database
                    printf("Thread %d writing to database...\n", 
                            GetCurrentThreadId());
                    dwCount++;
                } 

                __finally { 
                    // Release ownership of the mutex object
                    if (! ReleaseMutex(ghMutex)) 
                    { 
                        // Handle error.
                    } 
                } 
                break; 

            // The thread got ownership of an abandoned mutex
            // The database is in an indeterminate state
            case WAIT_ABANDONED: 
                return FALSE; 
        }
    }
    return TRUE; 
}
 
Share this answer
 
v3
I would think that the obvious solution would be to use std::mutex[^] and std::lock[^]

Best regards
Espen Harlinn
 
Share this answer
 
Comments
pasztorpisti 24-Mar-13 10:56am    
+5, a nice way with a C++11 compliant std lib.
pasztorpisti 25-Mar-13 5:32am    
Sorry, after commenting I forgot to give the five, just compensated. :-)
Sergey Alexandrovich Kryukov 25-Apr-13 18:57pm    
Fair enough, a 5. Agree with pasztorpisti about the use of std...
—SA
Espen Harlinn 25-Apr-13 19:00pm    
Thank you, Sergey :-D
Just to make things clear, in threading tutorials the terms "mutex", "critical section" and "lock" are exchangeable, they mean the same. There are several kinds of them on each os. The kinds I usually use are the following:
- spin lock
- normal lock between the threads of a single process
- inter-process lock to synchronize between any threads of any processes, for example if you share data via shared memory between several processes

Spin locks are evil, they are usually used only in case of hardcore optimizations and in case where the time a thread holds a lock is extremely small. You may never need it in your life but good to know about its existence. A normal lock is perfect to synchronize access to a shared resource between several threads inside your program. This is the kind of lock you will use in most cases, it is usually better in performance than an interprocess lock, so dont use interprocess locks when you don't need them. In windows the name of the normal lock is CRITICAL_SECTION, while the name of the inter-process lock is Mutex. Don't get confused by these names, they could have given the "Mutex" name to the normal lock if they wanted to do so, they have just used synonyms to differentiate them. So in most cases you will use the CRITICAL_SECTION in windows. Each platform has different thread synchronization primitives and in case of performance critical scenarios you have to find out or test out which one works best for your case.

Another important property of locks is whether they are recursive or not. For example the windows CRITICAL_SECTION is recursive by default. This means that after a thread acquires the lock for the first time, it can acquire the lock as many times as it wants without blocking, but it must release the lock the same number of times as it was acquired. A non-recursive lock is simpler and usually a bit faster than a recursive one but if your thread tries to acquire it for the second time without releasing the first, it gets into deadlock.

This is how I implement a normal lock on windows:
C++
class CLock
{
public:
    CLock()
    {
        InitializeCriticalSection(&m_CriticalSection);
    }
    ~CLock()
    {
        DeleteCriticalSection(&m_CriticalSection);
    }
    void Acquire()
    {
        EnterCriticalSection(&m_CriticalSection);
    }
    void Release()
    {
        LeaveCriticalSection(&m_CriticalSection);
    }
private:
    CLock(const CLock&);    // disabling copy constructor
    CRITICAL_SECTION m_CriticalSection;
};


// Using RAII to make unlocking cleaner and safer
class CAutoLock
{
public:
    CAutoLock(CLock& lock)
        : m_Lock(lock)
    {
        m_Lock.Acquire();
    }
    ~CAutoLock()
    {
        m_Lock.Release();
    }
private:
    CLock &m_Lock;
};

#define CONCAT_MACRO1(var_name, counter) var_name##counter
#define CONCAT_MACRO2(var_name, counter) CONCAT_MACRO1(var_name, counter)
#define CONCAT_COUNTER(var_name) CONCAT_MACRO2(var_name, __COUNTER__)
// automatic name generation for the __auto_lock stack object
#define LOCK_SCOPE(var) CAutoLock CONCAT_COUNTER(__auto_lock)(var)


CLock m_Lock2;

class CTest
{
public:
    void DoJob()
    {
        {
            LOCK_SCOPE(m_Lock1);
            LOCK_SCOPE(m_Lock2);

            // Do job with resources protected by m_Lock1 and m_Lock2

        } // both m_Lock2 and m_Lock1 are released at the end of the scope or if you
          // return from the function in reverse order of how you acquired them

        LOCK_SCOPE(m_Lock1);

        bool success = DoJob2();
        if (!success)
            return; // m_Lock1 is released automatically

        // do something

    } // m_Lock1 is released automatically

private:
    bool DoJob2()
    {
        // anything you want
        return true;
    }

private:
    CLock m_Lock1;
};

In small programs its ok to use pre-baked lock classes from mfc or boost or the standard library. However if you make something serious then your own implementation can help you a lot. You can put debug stuff into your CLock or CMutex or whatever classes. For example in a non-recursive lock class you can ASSERT when the same thread tries to acquire the lock for the second time making it easier to catch big mistakes/deadlocks early. In a spinlock class you can count the number of spins to find out if a spinlock somewhere is effective or not...
 
Share this answer
 
v2
Comments
Sergey Alexandrovich Kryukov 25-Apr-13 18:55pm    
Good job, a 5.
It's good that you mentioned critical section. Windows Mutex, in most cases, would be an overkill. Critical section is light weight, so it's better for in-proc things (where the mutex could be unnamed).
—SA
pasztorpisti 26-Apr-13 2:37am    
Thank you! This can be a subject of an "advanced" windows specific threading question, many people who learn from textbook refer only to mutex (as its name is the same used in most textbooks).
Sergey Alexandrovich Kryukov 26-Apr-13 10:02am    
Well, perhaps this is because the "critical section" is a mutex...
—SA
Espen Harlinn 25-Apr-13 19:00pm    
Well answered :-D
pasztorpisti 26-Apr-13 2:38am    
Thanks, I tried to do my best! :-)
First of all, in distinction to spinlock, mutex must use futex system call in kernel to wait while other threads lock the mutex, while spinlock just loops in cycle and occupies processor in that case.
Also all operations on shared variables must be atomic(including atomic compare_and_exchange) because other threads might do those operations at the same time. If, for example, increment of integer value is not atomic, then following might happen:
count = 0;
thread 1 reads count(0);
thread 2 reads count(0);
thread1 increments count to 1;
thread2 increments count to 1;
thread1 writes count(1);
thread2 writes count(1);
count value is 1 now, not 2;
Note that read, increment and write is a single x86 processor command for integer type, but it is still non-atomic without 'lock' prefix.
Here is a source code in C++ with comments:
[cpmutex.cpp]
 
Share this answer
 
v7
Comments
Dave Kreskowiak 9-Nov-17 13:42pm    
Really? This question is over 4 years old and already has a bunch of accepted answers. Don't resurrect old questions.
CHill60 9-Nov-17 15:59pm    
This is essentially spam. I won't report it this time but don't do it again
O(LogN) 10-Nov-17 8:41am    
Noone really answered about _implementation_ of mutex, especially with source for Linux. I think answer is useful.
CHill60 10-Nov-17 17:09pm    
That's ok. Post an actual answer instead of a link to your own work. OR stick to answering newer questions where the OP still needs help OR write an article on the subject
class Mutex
{
    private:
        volatile unsigned long long interlock;
    public:
        Mutex();
        ~Mutex();
    private:
        // These should be private to prevent locks being obtained
        // in a way that makes them exception unsafe.
        void lock();
        void unlock();

        // Need a friend that can lock and unlock the Mutex safely and correctly.
        friend class Locker;
};

class Locker
{
     Mutex&    m;
     public:
         Locker(Mutex& m): m(m) {m.lock();}
         ~Locker()              {m.unlock();}
};

// Usage
int main()
{
    Mutex    m;
    for(;;)
    {
        Locker lock(m);   // Locks the mutex (and gurantees it will be released)
                          // Even if there is an exception
    }
}
 
Share this answer
 
Comments
Richard MacCutchan 17-Sep-19 6:57am    
Apart from the fact that the question was answered 6 years ago, you have not provided an implementation.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900