LinuxC高级编程——线程间同步

 LinuxC高级编程——线程间同步


宗旨:技术的学习是有限的,分享的精神是无限的。


1、 互斥锁mutex

        多个线程同时访问共享数据时可能会冲突。对于多线程的程序,访问冲突的问题是很普遍的,解决的办法是引入互斥锁(Mutex, Mutual Exclusive Lock),获得锁的线程可以完成“读-修改-写”的操作,然后释放锁给其它线程,没有获得 锁的线程只能等待而不能访问共享数据,这样“读-修改-写”三步操作组成一个原子操作,要么都执行,要么都不执行,不会执行到中间被打断,也不会在其它处理器上并行做这个操作。Mutex用pthread_mutex_t类型的变量表示,可以这样初始化和销毁:

#include
int pthread_mutex_destroy(pthread_mutex_t *mutex);
int pthread_mutex_init(pthread_mutex_t *restrict mutex,constpthread_mutexattr_t *restrict attr);
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

        返回值:成功返回0,失败返回错误号。

pthread_mutex_init函数对Mutex做初始化,参数attr设定Mutex的属性,如果attr为NULL则表示缺省属性,用pthread_mutex_init函 数初始化的Mutex可以用pthread_mutex_destroy销毁。如果Mutex变量是静态分配的(全局变量 或static变量),也可以用宏定义PTHREAD_MUTEX_INITIALIZER来初始化,相当于用pthread_mutex_init初始化并且attr参数为NULL。 Mutex的加锁和解锁操作可以用下列函数:

#include
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);

        返回值:成功返回0,失败返回错误号。

        一个线程可以调用pthread_mutex_lock获得Mutex,如果这时另一个线程已经调用pthread_mutex_lock获得了该Mutex,则当前线程需要挂起等待,直到另一个线程调用pthread_mutex_unlock释放Mutex,当前线程被唤醒,才能获得该Mutex并继续执行。 如果一个线程既想获得锁,又不想挂起等待,可以调用pthread_mutex_trylock,如果Mutex已经被另一个线程获得,这个函数会失败返回EBUSY,而不会使线程挂起等待。 

#include 
#include 
#include 

#define NLOOP 5000
int counter; /* incremented by threads */

pthread_mutex_t counter_mutex = PTHREAD_MUTEX_INITIALIZER;

void *doit(void *);

int main(int argc, char **argv)
{
  pthread_t tidA, tidB;
  pthread_create(&tidA, NULL, doit, NULL);
  pthread_create(&tidB, NULL, doit, NULL);
  /* wait for both threads to terminate */
  pthread_join(tidA, NULL);
  pthread_join(tidB, NULL);
  return 0;
}
void *doit(void *vptr)
{
  int i, val;

  for (i = 0; i < NLOOP; i++)
  {
    pthread_mutex_lock(&counter_mutex);
    val = counter;
    printf("%x: %d\n", (unsigned int)pthread_self(), val   + 1);
    counter = val + 1;
    pthread_mutex_unlock(&counter_mutex);
  }
  return NULL;
}

2、 条件变量

        线程A需要等某个条件成立才能继续往下执行,现在这个条件不成立,线程A就阻塞等待,而线程B在执行过程中使这个条件成立了,就唤醒线程A继续执行。 在pthread库中通过条件变量(Condition Variable) 来阻塞等待一个条件,或者唤醒等待这个条件的线程。 Condition Variable用pthread_cond_t类型的变量表示,可以这样初始化和销毁:

#include 
int pthread_cond_destroy(pthread_cond_t *cond);
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t*restrict attr);
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

        返回值:成功返回0,失败返回错误号。

        和Mutex的初始化和销毁类似,pthread_cond_init函数初始化一个Condition Variable, attr参数 为NULL则表示缺省属性, pthread_cond_destroy函数销毁一个Condition Variable。如果Condition Variable是静态分配的,也可以用宏定义PTHEAD_COND_INITIALIZER初始化,相当于 用pthread_cond_init函数初始化并且attr参数为NULL。 Condition Variable的操作可以用下列函数:

#include 
int pthread_cond_timedwait(pthread_cond_t *restrict cond,pthread_mutex_t*restrict mutex,
const struct timespec *restrict abstime);
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrictmutex);
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);

        返回值:成功返回0,失败返回错误号。 可见,一个ConditionVariable总是和一个Mutex搭配使用的。一个线程可以调 用pthread_cond_wait在一个Condition Variable上阻塞等待,这个函数做以下三步操作:

1.  释放Mutex   2. 阻塞等待   3. 当被唤醒时,重新获得Mutex并返回.

        pthread_cond_timedwait函数还有一个额外的参数可以设定等待超时,如果到达了abstime所指定的 时刻仍然没有别的线程来唤醒当前线程,就返回ETIMEDOUT。一个线程可以调 用pthread_cond_signal唤醒在某个Condition Variable上等待的另一个线程,也可以调用pthread_cond_broadcast唤醒在这个Condition Variable上等待的所有线程。 

#include 
#include 
#include 

struct msg
{
  struct msg *next;
  int num;
};

struct msg *head;
pthread_cond_t has_product = PTHREAD_COND_INITIALIZER;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

void *consumer(void *p)
{
  struct msg *mp;
  for (;;)
  {
    pthread_mutex_lock(&lock);
    while (head == NULL)
    {
      pthread_cond_wait(&has_product, &lock);
    }
    mp = head;
    head = mp->next;
    pthread_mutex_unlock(&lock);
    printf("Consume %d\n", mp->num);
    free(mp);
    sleep(rand() % 5);
  }
}
void *producer(void *p)
{
  struct msg *mp;
  for (;;)
  {
    mp = malloc(sizeof(struct msg));
    mp->num = rand() % 1000 + 1;
    printf("Produce %d\n", mp->num);
    pthread_mutex_lock(&lock);
    mp->next = head;
    head = mp;
    pthread_mutex_unlock(&lock);
    pthread_cond_signal(&has_product);
    sleep(rand() % 5);
  }
}

int main(int argc, char *argv[])
{
  pthread_t pid, cid;
  srand(time(NULL));
  pthread_create(&pid, NULL, producer, NULL);
  pthread_create(&cid, NULL, consumer, NULL);
  pthread_join(pid, NULL);
  pthread_join(cid, NULL);

  return 0;
}

3、 信号量

        Mutex变量是非0即1的,可看作一种资源的可用数量,初始化时Mutex是1,表示有一个可用资源,加锁时获得该资源,将Mutex减到0,表示不再有可用资源,解锁时释放该资源,将Mutex重新加到1,表示又有了一个可用资源。信号量(Semaphore)和Mutex类似,表示可用资源的数量,和Mutex不同的是这个数量可以大于1.

#include 
int sem_init(sem_t *sem, int pshared, unsigned int value);
int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);
int sem_post(sem_t * sem);
int sem_destroy(sem_t * sem);

        semaphore变量的类型为sem_t, sem_init()初始化一个semaphore变量, value参数表示可用资源 的数量, pshared参数为0表示信号量用于同一进程的线程间同步,本节只介绍这种情况。在用 完semaphore变量之后应该调用sem_destroy()释放与semaphore相关的资源。 调用sem_wait()可以获得资源,使semaphore的值减1,如果调用sem_wait()时semaphore的值已 经是0,则挂起等待。如果不希望挂起等待,可以调用sem_trywait()。调用sem_post()可以释放资 源,使semaphore的值加1,同时唤醒挂起等待的线程。

#include 
#include 
#include 
#include 

#define NUM 5
int queue[NUM];
sem_t blank_number, product_number;

void *producer(void *arg)
{
  int p = 0;
  while (1)
  {
    sem_wait(&blank_number);
    queue[p] = rand() % 1000 + 1;
    printf("Produce %d\n", queue[p]);
    sem_post(&product_number);
    p = (p + 1) % NUM;
    sleep(rand() % 5);
  }
}

void *consumer(void *arg)
{
  int c = 0;
  while (1)
  {
    sem_wait(&product_number);
    printf("Consume %d\n", queue[c]);
    queue[c] = 0;
    sem_post(&blank_number);
    c = (c + 1) % NUM;
    sleep(rand() % 5);
  }
}

int main(int argc, char *argv[])
{
  pthread_t pid, cid;
  sem_init(&blank_number, 0, NUM);
  sem_init(&product_number, 0, 0);
  pthread_create(&pid, NULL, producer, NULL);
  pthread_create(&cid, NULL, consumer, NULL);
  pthread_join(pid, NULL);
  pthread_join(cid, NULL);
  sem_destroy(&blank_number);
  sem_destroy(&product_number);
  return 0;
}

你可能感兴趣的:(Linux,C高级编程,Linux,C高级编程)