Linux系统编程多线程之条件变量和信号量讲解
一.前言
生产者消费者模型引入
/*简单的生产者消费者模型
如何查看错误:ulimit -a发现core file size (blocks, -c) 0生成core文件ulimit -c unlimitedgcc producust.c -o pro -lpthread -g编译后产生段错误发现没有生成core文件cat /proc/sys/kernel/core_pattern如果输出的是一个路径(例如 /var/crash/core.%e.%p),核心文件可能会保存在指定的位置,而不是当前工作目录设置核心文件始终生成在当前目录sudo sysctl -w kernel.core_pattern=core再次运行pro,即生成core文件
*/
/*生产者消费者模型(粗略的版本)
*/
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>struct Node{int num;struct Node *next;
};//创建互斥量
pthread_mutex_t mutex;
// 头结点
struct Node * head = NULL;void * producer(void * arg) {// 不断的创建新的节点,添加到链表中while(1) {pthread_mutex_lock(&mutex);struct Node * newNode = (struct Node *)malloc(sizeof(struct Node));newNode->next = head;head = newNode;newNode->num = rand() % 1000;printf("add node, num : %d, tid : %ld\n", newNode->num, pthread_self());pthread_mutex_unlock(&mutex);usleep(100);}return NULL;
}void * customer(void * arg) {while(1) {pthread_mutex_lock(&mutex);// 保存头结点的指针struct Node * tmp = head;if(head!=NULL){head = head->next;printf("del node, num : %d, tid : %ld\n", tmp->num, pthread_self());free(tmp);pthread_mutex_unlock(&mutex);usleep(100);}else{pthread_mutex_unlock(&mutex);}}return NULL;
}int main() {pthread_mutex_init(&mutex,NULL);// 创建5个生产者线程,和5个消费者线程pthread_t ptids[5], ctids[5];for(int i = 0; i < 5; i++) {pthread_create(&ptids[i], NULL, producer, NULL);pthread_create(&ctids[i], NULL, customer, NULL);}for(int i = 0; i < 5; i++) {pthread_detach(ptids[i]);pthread_detach(ctids[i]);}while(1){sleep(10);}pthread_mutex_destroy(&mutex);pthread_exit(NULL);return 0;
}
二.条件变量
条件变量不是锁,只是配合我们的互斥锁去使用
条件变量的类型:pthread_cond_t
初始化
int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t*restrict attr);销毁
int pthread_cond_destroy(pthread_cond_t*cond);一直等待,并且需要通知解除
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t*restrict mutex);等待多长的时间
int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t*restrict mutex,const struct timespec *restrict abstime);唤醒一个或多个等待
int pthread_cond_signal(pthread_cond_t*cond);唤醒所有的
int pthread_cond_broadcast(pthread_cond_t*cond);
案例示范
/*
初始化
int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t*restrict attr);销毁
int pthread_cond_destroy(pthread_cond_t*cond);一直等待,并且需要通知解除
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t*restrict mutex);等待多长的时间
int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t*restrict mutex,const struct timespec *restrict abstime);唤醒一个或多个等待
int pthread_cond_signal(pthread_cond_t*cond);唤醒所有的
int pthread_cond_broadcast(pthread_cond_t*cond);
*/#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>struct Node{int num;struct Node *next;
};//创建互斥量
pthread_mutex_t mutex;
//创建条件变量
pthread_cond_t cond;
// 头结点
struct Node * head = NULL;void * producer(void * arg) {// 不断的创建新的节点,添加到链表中while(1) {pthread_mutex_lock(&mutex);struct Node * newNode = (struct Node *)malloc(sizeof(struct Node));newNode->next = head;head = newNode;newNode->num = rand() % 1000;printf("add node, num : %d, tid : %ld\n", newNode->num, pthread_self());//只要生产一个就通知消费者进行消费pthread_cond_signal(&cond);pthread_mutex_unlock(&mutex);usleep(100);}return NULL;
}void * customer(void * arg) {while(1) {pthread_mutex_lock(&mutex);// 保存头结点的指针struct Node * tmp = head;if(head!=NULL){head = head->next;printf("del node, num : %d, tid : %ld\n", tmp->num, pthread_self());free(tmp);pthread_mutex_unlock(&mutex);usleep(100);}else{//没有数据,阻塞等待//当wait这个函数调用阻塞时,会对这个互斥锁进行解锁,当不阻塞时会对这个互斥锁重新加锁pthread_cond_wait(&cond,&mutex);pthread_mutex_unlock(&mutex);}}return NULL;
}int main() {pthread_mutex_init(&mutex,NULL);pthread_cond_init(&cond,NULL);// 创建5个生产者线程,和5个消费者线程pthread_t ptids[5], ctids[5];for(int i = 0; i < 5; i++) {pthread_create(&ptids[i], NULL, producer, NULL);pthread_create(&ctids[i], NULL, customer, NULL);}for(int i = 0; i < 5; i++) {pthread_detach(ptids[i]);pthread_detach(ctids[i]);}while(1){sleep(10);}pthread_mutex_destroy(&mutex);pthread_cond_destroy(&cond);pthread_exit(NULL);return 0;
}
三.信号量
信号量:也是用于阻塞线程的
灯亮可以用,灯灭不可以用,不可以保证多线程数据安全问题,若需要保证,需要跟互斥锁一起使用
信号量的类型 sem_t
int sem_init(sem_t*sem,int pshared, unsigned int value);int sem_destroy(sem_t*sem);
int sem_wait(sem_t*sem);
int sem_trywait(sem_t*sem);
int sem_timedwait(sem_t*sem,const struct timespec *abs_timeout);
int sem_post(sem_t*sem);
int sem_getvalue(sem_t*sem,int*sval);
示范代码
/*
初始化
int sem_init(sem_t*sem,int pshared, unsigned int value);-参数:sem:信号量pshared:代表用在线程(0)之间还是进程(其他值)之间value:信号量中的值释放资源
int sem_destroy(sem_t*sem);等待
int sem_wait(sem_t*sem);-没调用一次对信号量值-1,若剩下的值=0阻塞,>0直接返回,调用post+1int sem_trywait(sem_t*sem);等待多长时间
int sem_timedwait(sem_t*sem,const struct timespec *abs_timeout);解锁信号量
int sem_post(sem_t*sem);-没调用一次对信号量+1int sem_getvalue(sem_t*sem,int*sval);
*/
#include <semaphore.h>
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>struct Node{int num;struct Node *next;
};//创建互斥量
pthread_mutex_t mutex;//创建两个信号量
sem_t psem,csem;// 头结点
struct Node * head = NULL;void * producer(void * arg) {// 不断的创建新的节点,添加到链表中while(1) {sem_wait(&psem);pthread_mutex_lock(&mutex);struct Node * newNode = (struct Node *)malloc(sizeof(struct Node));newNode->next = head;head = newNode;newNode->num = rand() % 1000;printf("add node, num : %d, tid : %ld\n", newNode->num, pthread_self());pthread_mutex_unlock(&mutex);sem_post(&csem);usleep(100);}return NULL;
}void * customer(void * arg) {while(1) {sem_wait(&csem);pthread_mutex_lock(&mutex);// 保存头结点的指针struct Node * tmp = head;head = head->next;printf("del node, num : %d, tid : %ld\n", tmp->num, pthread_self());free(tmp);pthread_mutex_unlock(&mutex);sem_post(&psem);}return NULL;
}int main() {sem_init(&psem,0,8);sem_init(&csem,0,0);pthread_mutex_init(&mutex,NULL);// 创建5个生产者线程,和5个消费者线程pthread_t ptids[5], ctids[5];for(int i = 0; i < 5; i++) {pthread_create(&ptids[i], NULL, producer, NULL);pthread_create(&ctids[i], NULL, customer, NULL);}for(int i = 0; i < 5; i++) {pthread_detach(ptids[i]);pthread_detach(ctids[i]);}while(1){sleep(10);}pthread_mutex_destroy(&mutex);pthread_exit(NULL);return 0;
}