linux mutex and condition variables

Why conditional variables?

See a thread waiting for some event

Note: This article is a version of linux c condition variables and mutex (mutex), not in C ++.

mutex: mutual exclusion (mutually exclusive)

1, mutex initialization, the following two ways.

  • Initialization method call: mutex is dynamically allocated using malloc, or when using shared memory allocation area.
  • Do not call the initialization method: When using a statically allocated.
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
  • Returns: 0 on success; failure errno

2, mutex destruction

int pthread_mutex_destroy(pthread_mutex_t *mutex);
  • Returns: 0 on success; failure errno
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);

3, locking and unlocking

int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
  • pthread_mutex_lock: locked. If it is not locked state, the lock does not block and returns. If you are already locked state, which back up here, and waits until unlocked.
  • pthread_mutex_trylock: try to lock. If it is not locked state, the lock does not block and returns. If a state has been locked, no block, return immediately, the return value EBUSY.

4, the function of condition variables 2

int pthread_cond_wait(pthread_cond_t *restrict cond,
           pthread_mutex_t *restrict mutex);
int pthread_cond_signal(pthread_cond_t *cond);
  • pthread_cond_wait:

    • This function is called the point of treatment:

      1, to unlock the mutex.

      2, the thread calling this function into sleep until some other thread calls pthread_cond_signal on this condition variable.

    • Treatment after being awakened: to re mutex lock before returning.

  • pthread_cond_signal: wake-up call function pthread_cond_wait thread

Condition variables commonly used mode of producers and consumers.

What is the producer and consumer model?

Version 1: All producers threads are executed in parallel, consumer thread is waiting for all producers after the end of the implementation of linear, consumers began to thread execution.

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>

#define MAXITEM  100000000
#define MAXTHREAD  100
#define min(x,y) ( x>y?y:x )

int nitem;

struct {
  pthread_mutex_t mutex;
  int buf[MAXITEM];
  int idx;
  int val;
}shared = {
  PTHREAD_MUTEX_INITIALIZER
};

void* produce(void*);
void* consume(void*);

int main(int argc, char** argv){
  int i;
  int nthreads;
  int count[MAXTHREAD];

  pthread_t tid_produce[MAXTHREAD], tid_consume;

  if(argc != 3){
    printf("arg error\n");
    return 1;
  }

  nitem = min(MAXITEM,atoi(argv[1]));
  nthreads = min(MAXTHREAD, atoi(argv[2]));

  for(i = 0; i < nthreads; ++i){
    count[i] = 0;
    pthread_create(&tid_produce[i], NULL, produce, &count[i]);
  }

  for(i = 0; i < nthreads; ++i){
    pthread_join(tid_produce[i], NULL);
    printf("cout[%d] = %d\n", i, count[i]);
  }

  pthread_create(&tid_consume, NULL, consume, NULL);
  pthread_join(tid_consume, NULL);

  return 0;
}

void* produce(void* arg){
  while(1){
    pthread_mutex_lock(&shared.mutex);
    if(shared.idx >= nitem){
      pthread_mutex_unlock(&shared.mutex);
      return NULL;
    }
    shared.buf[shared.idx] = shared.val;
    shared.idx++;
    shared.val++;
    pthread_mutex_unlock(&shared.mutex);
    *((int*)arg) +=1;
  }
}

void* consume(void* arg){
  int i;
  for(i = 0; i < nitem; ++i){
    if(shared.buf[i] != i){
      printf("buf[%d] = %d\n", i, shared.buf[i]);
    }
  }
}

Version 2: All the producer and consumer threads threads are executed in parallel. Then there will be a problem, is the case of the consumer thread is first executed, the producer thread has not produced the data, then the consumer can only loop to thread mutex lock and unlock. This became rotation (Spinning) or a polling (polling), a multi-CPU is a waste of time. We can sleep very short period of time, but do not know how long sleep. At this time, the condition variables debut.

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>

#define MAXITEM  100000000
#define MAXTHREAD  100
#define min(x,y) ( x>y?y:x )

int nitem;

struct {
  pthread_mutex_t mutex;
  int buf[MAXITEM];
  int idx;
  int val;
}shared = {
  PTHREAD_MUTEX_INITIALIZER
};

void* produce(void*);
void* consume(void*);

int main(int argc, char** argv){
  int i;
  int nthreads;
  int count[MAXTHREAD];

  pthread_t tid_produce[MAXTHREAD], tid_consume;

  if(argc != 3){
    printf("arg error\n");
    return 1;
  }

  nitem = min(MAXITEM,atoi(argv[1]));
  nthreads = min(MAXTHREAD, atoi(argv[2]));

  for(i = 0; i < nthreads; ++i){
    count[i] = 0;
    pthread_create(&tid_produce[i], NULL, produce, &count[i]);
  }
  pthread_create(&tid_consume, NULL, consume, NULL);
  
  for(i = 0; i < nthreads; ++i){
    pthread_join(tid_produce[i], NULL);
    printf("cout[%d] = %d\n", i, count[i]);
  }
  pthread_join(tid_consume, NULL);

  return 0;
}

void* produce(void* arg){
  while(1){
    pthread_mutex_lock(&shared.mutex);
    if(shared.idx >= nitem){
      pthread_mutex_unlock(&shared.mutex);
      return NULL;
    }
    shared.buf[shared.idx] = shared.val;
    shared.idx++;
    shared.val++;
    pthread_mutex_unlock(&shared.mutex);
    *((int*)arg) +=1;
  }
}

void consume_wait(int i){
  while(1){
    pthread_mutex_lock(&shared.mutex);
    if(i < shared.idx){
      pthread_mutex_unlock(&shared.mutex);
      return;
    }
    pthread_mutex_unlock(&shared.mutex);
  }
}

void* consume(void* arg){
  int i;
  for(i = 0; i < nitem; ++i){
    consume_wait(i);
    if(shared.buf[i] != i){
      printf("buf[%d] = %d\n", i, shared.buf[i]);
    }
  }
  return NULL;
}

Version 3: All the producer and consumer threads threads are executed in parallel. Version 2 of the polling solve the problem. Use condition variables.

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>

#define MAXITEM  100000000
#define MAXTHREAD  100
#define min(x,y) ( x>y?y:x )

int nitem;
int buf[MAXITEM];

struct {
  pthread_mutex_t mutex;
  int idx;
  int val;
} shared = {
  PTHREAD_MUTEX_INITIALIZER
};

struct {
  pthread_mutex_t mutex;
  pthread_cond_t  cond;
  int nready;
} nready = {
  PTHREAD_MUTEX_INITIALIZER,
  PTHREAD_COND_INITIALIZER
};

void* produce(void*);
void* consume(void*);

int main(int argc, char** argv){
  int i;
  int nthreads;
  int count[MAXTHREAD];

  pthread_t tid_produce[MAXTHREAD], tid_consume;

  if(argc != 3){
    printf("arg error\n");
    return 1;
  }

  nitem = min(MAXITEM,atoi(argv[1]));
  nthreads = min(MAXTHREAD, atoi(argv[2]));

  for(i = 0; i < nthreads; ++i){
    count[i] = 0;
    pthread_create(&tid_produce[i], NULL, produce, &count[i]);
  }
  pthread_create(&tid_consume, NULL, consume, NULL);
  
  for(i = 0; i < nthreads; ++i){
    pthread_join(tid_produce[i], NULL);
    printf("cout[%d] = %d\n", i, count[i]);
  }
  pthread_join(tid_consume, NULL);

  return 0;
}

void* produce(void* arg){
  while(1){
    pthread_mutex_lock(&shared.mutex);
    if(shared.idx >= nitem){
      pthread_mutex_unlock(&shared.mutex);
      return NULL;
    }
    buf[shared.idx] = shared.val;
    shared.idx++;
    shared.val++;
    pthread_mutex_unlock(&shared.mutex);

    pthread_mutex_lock(&nready.mutex);
    if(nready.nready == 0){
      pthread_cond_signal(&nready.cond);//--------------②
    }
    nready.nready++;
    pthread_mutex_unlock(&nready.mutex);//--------------③

    *((int*) arg) += 1;
  }
}

void* consume(void* arg){
  int i;
  for(i = 0; i < nitem; ++i){
    pthread_mutex_lock(&nready.mutex);
    while(nready.nready == 0){//--------------①
      pthread_cond_wait(&nready.cond, &nready.mutex);
    }
    nready.nready--;
    pthread_mutex_unlock(&nready.mutex);

    if(buf[i] != i){
      printf("buf[%d] = %d\n", i, buf[i]);
    }
  }
  printf("buf[%d] = %d\n", nitem-1, buf[nitem-1]);
}

Best practices for mutex and condition variable:

1, to be shared by multiple threads of data definition and meaning in a mutually exclusive lock structure in.

2, the condition variable, mutexes, and critical conditions are defined in a structure.

3, ① in place, and finally do not use if, on the grounds that, after pthread_cond_wait return, there may be another consumer thread it consumed, so you want to test again the appropriate conditions are satisfied or not, to prevent the occurrence of false (spurious) wake. All kinds of thread should try to minimize these false wake, but still occur.

4, the code pthread_cond_signal attention at ②, imagine the worst case, after the function is called, another thread immediately waiting to be awakened, they were awakened pthread_cond_wait function to be locked immediately, but the call pthread_cond_signal function of the thread has not executed to pthread_mutex_unlock ③ at, so the thread to be awakened and immediately terminated. Therefore, in order to avoid this from happening, at the code pthread_cond_signal ② ③ in the next line at.

Reference to the following pseudocode:

int flag;    
pthread_mutex_lock(&nready.mutex);
int = nready.nready == 0);
nready.nready++;
pthread_mutex_unlock(&nready.mutex);

if(flag){
  pthread_cond_signal(&nready.cond);
}

Guess you like

Origin www.cnblogs.com/xiaoshiwang/p/11041070.html