2

I'm new to multi-threads programming and I got confused about where to declare mutex. I got the idea of mutex, lock/unlock by googling a lot. But I still don't know where I need to declare the pthread_mutex_t variable, and what's the difference.

For example here is Case 1:

#include <pthread.h>

pthread_mutex_t count_mutex;
long long count;

void
increment_count()
{
        pthread_mutex_lock(&count_mutex);
    count = count + 1;
        pthread_mutex_unlock(&count_mutex);
}

Here is Case 2:

struct order_que
{
struct order **orders;
int size;
int head;
int tail;
pthread_mutex_t lock;
};

void *ClientThread(void *arg)
{
struct client_arg *ca = (struct client_arg *)arg;
int i;
for(i=0; i < ca->order_count; i++) {
    ......
    queued = 0;
    while(queued == 0) {
        pthread_mutex_lock(&(ca->order_que->lock));
        ......
        if(next == ca->order_que->tail) {
            pthread_mutex_unlock(&(ca->order_que->lock));
            continue;
        }
        ......
        pthread_mutex_unlock(&(ca->order_que->lock));
        ......
    }
}
return(NULL);
}

Could anyone tell me what's the difference between these two cases and why I need declare the mutex in this way?

Musa
  • 96,336
  • 17
  • 118
  • 137
  • The first case uses a single global mutex. Simple but not flexible. The second case allows different mutexes to be used for different threads as it is passed to the thread as part of its `arg`. For example, you may have two different queues that have seperate locking mutexes. Then the second method is more flexible as that same thread function can be used without changing any of its code - just need to invoke it with different `arg` values. – kaylum Dec 15 '15 at 02:51
  • 1
    You declare it where it's needed, like any other instance of some var type. – Martin James Dec 15 '15 at 02:51
  • 1
    In summary, they are both valid ways to set up the mutex. It's a matter of understanding their differences and knowing which is more appropriate to use to solve a particular problem. – kaylum Dec 15 '15 at 02:53
  • Imagine `pthread_mutex_t lock;` was actually `int i;`, and `pthread_mutex_lock` was `++` and `pthread_mutex_unlock` was `--`, or something like that. Do you know the difference now? – user253751 Dec 15 '15 at 02:56
  • By the way, in the second code block, there is no "*the* mutex". – user253751 Dec 15 '15 at 02:56
  • Please read the man page about pthread_mutex. Amongst other things the man page clearly states that there are two ways to initialized the mutex. Use one of those ways. ( I suggest using an assignment statement `pthread_mutex_t count_mutex=PTHREAD_MUTEX_INITIALIZER;`) – user3629249 Dec 15 '15 at 08:52
  • Mutex is used for mutual exclusion of access of critical sections. For Case 1 the critical section is the increment of a global variable, i.e. count. Case 2 seems to have mutex lock that is a member of a structure. The structure is a queue and the mutex lock is per queue. That means the critical section is per queue. Also case 1’s critical section can be resolved using atomic add like GCC’s __sync_add_and_fetch() without using mutex. – Raunak Mukhia Dec 15 '15 at 13:45

1 Answers1

1

Could anyone tell me what's the difference between these two cases and why I need declare the mutex in this way?

Generally speaking, mutex is designed to synchronize accesses (protect against race conditions) to a resource. Thus mutex declaration often follows the declaration of the resource it is intended to protect.

In case #1, the mutex syncs accesses to the global variable count - and thus it is declared globally, along with the variable. It guarantees that, when the increment_count() is called on different CPUs from different threads, the non-atomic arithmetics on the count variable would be performed in a consistent fashion, producing expected results.

In case #2, the mutex syncs accesses to the order_que ring buffer which (apparently) might be accessed from multiple threads. (Appears to be code for a job queue: the queue of items/jobs/etc, the threads should process in parallel.) The generic ring buffer requires arithmetics on the head and the tail pointers to enqueue and dequeue items. To guarantee the consistent results of the arithmetics on the head/tail (missing from your example) the mutex is used to synchronize the accesses to them. Thus the mutex is declared in the same context as the variables.

Dummy00001
  • 16,630
  • 5
  • 41
  • 63