Semaphore signalling vs mutex

2019-09-18 04:12发布

问题:

I was looking over some topics that describes the difference between mutex and binary semaphore. In many topics it is stated that semaphore act as a signalling mechanism i.e if a thread has locked a semaphore then another thread can unlock(release) the semaphore(acting as signal). But in case of mutex only the thread that locks the mutex can unlock it .It can't be unlocked by any other thread and if other thread tries to unlock it this will return error.

I have tried writing a code that uses mutex for synchronization. In the code I am locking the mutex in one thread and successfully unlocking the mutex in other thread. Can someone please explain how this can happen.According to the answers everywhere the other thread should not be able to unlock mutex.

Any suggestions are most welcome.

回答1:

Typical POSIX thread implementations do not perform any checks or validations that the thread unlocking a mutex is the same as the one that locked that (or likely even that the mutex was locked in the first place). This is not how mutexes are supposed to be used. The word "mutex" means mutual exclusion; the purpose of the concept is to provide a mechanism for multiple threads to safely access or modify a resource (e.g. data structure) without other threads interfering, and when done a modification have the resource in consistent state before unlocking the mutex.

It could be nice to catch such programming errors in run time (just like catching out of bounds writes to arrays), but by default providing such book-keeping and guards are too expensive in terms of performance; mutex locking and unlocking operations are supposed to give you minimal overhead for them to be usable. This is the reason you are succeeding in unlocking your mutexes from a wrong thread. In standard Linux pthread implementation you could use "error checking" mutexes, which would give you error when you try to unlock a mutex from a wrong thread, but again this check exists only to catch programming errors.

I am not able to advice you in compact form about what to use for and how to implement your signaling without better understanding of the nature of your signaling need.



回答2:

Any suggestions are most welcome.

I have had no problems using this Posix Process Semaphore encapsulation for std::thread as well as posix.

//
#include <semaphore.h>  // posix semaphore

class PPLSem_t // Posix Process Semaphore, set to Local mode (unnamed, unshared)
{
public: //           not shared-between-process-v  v--initial-value-unlocked
   PPLSem_t()   { int stat = ::sem_init(&m_sem, 0, 1);  assert(0 == stat); } // ctor
   ~PPLSem_t()  { int stat = ::sem_destroy(&m_sem);     assert(0 == stat); } // dtor

   int lock()   { return (::sem_wait(&m_sem)); }   // returns 0 when success, else -1
   int unlock() { return (::sem_post(&m_sem)); }   // returns 0 when success, else -1
private:
   ::sem_t m_sem;
};

There is no restriction of which thread might signal it.



回答3:

According to pthread_mutex_lock man page: If the mutex type is PTHREAD_MUTEX_DEFAULT, attempting to unlock the mutex if it was not locked by the calling thread results in undefined behavior. Attempting to unlock the mutex if it is not locked results in undefined behavior.

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <errno.h>
#include <syscall.h>

pthread_mutex_t mutex;

void *thread(void *ptr)
{
    int ret = pthread_mutex_unlock(&mutex);

    if (!ret)
    {
        printf("error unlocking: %s", strerror(errno));
    }

    printf("tid: %u - owner: %u\n", syscall(SYS_gettid), mutex.__data.__owner);

    return NULL;
}

int main()
{
    pthread_t thread1;
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
    pthread_mutex_init(&mutex, &attr);
    pthread_mutexattr_destroy(&attr);

    pthread_mutex_lock(&mutex);

    printf("tid: %u - owner: %u\n", syscall(SYS_gettid), mutex.__data.__owner);

    pthread_create(&thread1, NULL, thread, NULL);

    pthread_join(thread1, NULL);

    return 0;
}

Output for me:

tid: 13987 - owner: 13987
tid: 13992 - owner: 13987

The sole difference between mutex and binary semaphore is the owner concept. Mutex has an owner, so you "must" lock and unlock it in the same thread, but you can post or wait semaphore in different threads.