Home > Net >  C Mutex Lock/Unlock
C Mutex Lock/Unlock

Time:08-01

I'm experiencing some issues in the process of introducing mutex into my application, to enable multi-thread compatibilities.

I've got one thread for building user data, and another thread for rendering data. lets call these two threads as thread-1 and thread-2.

  1. I created the data in thread-1
  2. Then invoked lock() and pushed the data into array,
  3. Then invoked unlock() on thread-1
  4. Then tried to read the data written in thread-1 , fromthread-2using the following steps :
  5. Invoked lock() on thread-2
  6. loop the array display the user data
  7. Invoked unlock() in thread-2

Since thread-2 is faster than thread-1 it causes a lag in the rendering process. Removing the lock() and unlock() from thread-2 solves that issue. Can anyone please explain what's the reason for this?

Thanks in advanced!

CodePudding user response:

You could try the spin lock, and spin lock could used in those situation where the threads have more freqence switch and more compute task in short time, spin lock make the thread is looping itself and could not in sleeping, here are example code below, you could run on you machine and check the result:

#include <pthread.h>
#include <iostream>
#include <unistd.h>
#include <sys/time.h>

pthread_mutex_t mutex;
pthread_spinlock_t spinlock;

pthread_t tid_1;
pthread_t tid_2;

using namespace std;

#define LOOP_NUM 100000

#define MUTEX_LOCK

#ifdef MUTEX_LOCK

void* thread_func(void *param) {

    int *p = (int *)param;
    int i = 0;
    while( i   < LOOP_NUM) {

      pthread_mutex_lock(&mutex);

      std::cout <<   (*p) << std::endl;

      pthread_mutex_unlock(&mutex);
    }
}

#else

void* thread_func(void *param) {

    int *p = (int *)param;
    int i = 0;
    while( i   < LOOP_NUM) {

      pthread_spin_lock(&spinlock);

      std::cout <<   (*p)  << std::endl;

      pthread_spin_unlock(&spinlock);
    }
}

#endif


int main() {

    int i = 0;

    struct timeval tv_start;
    struct timeval tv_end;

    gettimeofday(&tv_start, NULL);

#ifdef MUTEX_LOCK
    pthread_mutex_init(&mutex, NULL);
#else    
    pthread_spin_init(&spinlock, 0);
#endif    

    pthread_create(&tid_1, NULL, thread_func, &i);

    pthread_create(&tid_2, NULL, thread_func, &i);

    pthread_join(tid_1, NULL);
    pthread_join(tid_2, NULL);

    gettimeofday(&tv_end, NULL);

    std::cout<< "spend time:" << (tv_end.tv_sec-tv_start.tv_sec) * 1000 \
      (tv_end.tv_usec - tv_start.tv_usec)/1000  << " ms" << std::endl;

#ifdef MUTEX_LOCK
    pthread_mutex_destroy(&mutex);
#else
    pthread_spin_destroy(&spinlock);
#endif        


}

Here are the result, when I run 10 times by the mutex lock (millisecond): 6156 5989 5851 5489 8249 6215 5921 6361 6346 6434

And here are the result using spin lock(millisecond): 5434 4489 4713 5519 4868 4832 5287 5670 5270 5200

You will find that using spin lock is faster than mutex lock, nearly 1 second on my machine.

CodePudding user response:

If you haven't already done so, you should measure how much time each thread is spending with the mutex locked, and minimize that amount of time as much as possible. That will reduce the amount of time the other thread might have to spend waiting to acquire the lock.

One straightforward way to minimize the amount of time either thread spends holding the lock is to use double-buffering, i.e. give each thread its own private data-structure, plus a third data-structure that is shared between the two threads and serialized by the mutex.

Then, instead of each thread doing:

1. lock mutex
2. read or write to data-structure (which could take some time)
3. unlock mutex

... your writer would do this:

1. Write new data to writer's private data-structure (which could take some time)
2. lock mutex
3. swap writer's private data structure with shared data structure (fast!)
4. unlock mutex 
5. clear writer's private data structure

... and your reader would do this:

1. clear reader's private data structure
2. lock mutex
3. swap reader's private data structure with shared data structure (fast)
4. unlock mutex
5. Use data in reader's private data structure (which could take some time)

Most data structures can do a swap-operation in O(1) time (i.e. by swapping just two internal pointers) so this allows lock-contention to be extremely minimal in all cases. (If your performance requirements are so fine-grained that even doing context-switches is a potential problem, you could go further and replace the mutex with a spinlock, but in most cases that's not strictly necessary)

  • Related