LinuxC高级编程——线程间同步编程
宗旨:技术的学习是有限的,分享的精神是无限的。
多线程
一、 互斥锁mutex函数
多个线程同时访问共享数据时可能会冲突。对于多线程的程序,访问冲突的问题是很广泛的,解决的办法是引入互斥锁(Mutex, Mutual Exclusive Lock),得到锁的线程能够完成“读-修改-写”的操做,而后释放锁给其它线程,没有得到 锁的线程只能等待而不能访问共享数据,这样“读-修改-写”三步操做组成一个原子操做,要么都执行,要么都不执行,不会执行到中间被打断,也不会在其它处理器上并行作这个操做。Mutex用pthread_mutex_t类型的变量表示,能够这样初始化和销毁:post
#include<pthread.h> 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的加锁和解锁操做能够用下列函数:spa
#include<pthread.h> 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,而不会使线程挂起等待。 rest
#include <stdio.h> #include <stdlib.h> #include <pthread.h> #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; }
二、 条件变量code
线程A须要等某个条件成立才能继续往下执行,如今这个条件不成立,线程A就阻塞等待,而线程B在执行过程当中使这个条件成立了,就唤醒线程A继续执行。 在pthread库中经过条件变量(Condition Variable) 来阻塞等待一个条件,或者唤醒等待这个条件的线程。 Condition Variable用pthread_cond_t类型的变量表示,能够这样初始化和销毁:进程
#include <pthread.h> 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 <pthread.h> 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 <stdlib.h> #include <pthread.h> #include <stdio.h> 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; }
三、 信号量
Mutex变量是非0即1的,可看做一种资源的可用数量,初始化时Mutex是1,表示有一个可用资源,加锁时得到该资源,将Mutex减到0,表示再也不有可用资源,解锁时释放该资源,将Mutex从新加到1,表示又有了一个可用资源。信号量(Semaphore)和Mutex相似,表示可用资源的数量,和Mutex不一样的是这个数量能够大于1.
#include <semaphore.h> 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 <stdlib.h> #include <pthread.h> #include <stdio.h> #include <semaphore.h> #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; }