【Linux】线程总结:线程同步 -互斥锁,条件变量,信号量实现多生产者多消费者模型
来源:互联网 发布:方舟优化补丁有用吗 编辑:程序博客网 时间:2024/04/26 06:49
学习环境 : Centos6.5 Linux 内核 2.6
Linux线程部分总结分为两部分:(1)线程的使用 ,(2)线程的同步与互斥。
第一部分线程的使用主要介绍,线程的概念,创建线程,线程退出,以及线程的终止与分离。【完成】 地址:【Linux】线程总结:初识、创建、等待、终止、分离
第二部分主要介绍在多线程环境下,使用同步与互斥保护共享资源,有互斥锁,条件变量,信号量,以及读写锁。
第二部分开始 (第二部分,拖了快三个月,终于要出生了)
互斥量
描述
互斥变量用来保证同一时刻只有一个线程访问需要保护的代码,用来保证临界资源的数据一致性。本质上可理解为是一把锁,在访问共享资源之前申请锁,只有你一个拿着钥匙,别人要进入就必须等你出去后把钥匙交给他们才可以。一旦使用完毕就要释放锁资源,给别人一个机会。 在使用的时候要注意,申请锁资源与释放锁资源的顺序,避免死锁的产生。
接口
#include<pthread>/* 动态初始化 */int pthread_mutex_init( pthread_mutex_t* mutex, const pthread_mutexattr_t* mutexattr);/* 销毁 */int pthread_mutex_destroy( pthread_mutex_t* mutex);/* 申请锁资源 */int pthread_mutex_lock ( pthread_mutex_t* mutex);/* 非阻塞申请锁资源 */int pthread_mutex_trylock( pthread_mutex_t* mutex);/* 释放锁资源 */int pthread_mutex_unlock ( pthread_mutex_t* mutex);
上述接口的第一个参数 mutex 都是定义的 pthread_mutex_t
结构体指针,用来操作目标互斥锁。
互斥锁在使用之前必须初始化,pthread_mutex_init
函数用于动态初始化互斥锁,mutexattr 参数指定互斥锁的属性。还可以静态分配,设置为常量 PTHREAD_MUTEX_INITALIZER
。如果是动态初始化则在使用完成后需要调用pthread_mutex_destory
释放资源。
pthread_mutex_lock
函数以原子操作的方式给一个互斥锁加锁。如果目标互斥锁已经被锁上,该函数的其余调用者将被阻塞,直到互斥锁的占有者将其解锁。
pthread_mutex_trylock
函数非阻塞式加锁,如果已经被加锁时,该函数立即返回错误码 EBUSY。
pthread_mutex_unlock
函数以原子操作的方式的给一个互斥锁解锁。如果此时有其他线程在等待这个互斥锁,解锁后将会有一个线程获得该锁。
具体使用方法可参照博客:线程安全与线程不安全,有一个互斥量的简单示例。
条件变量
互斥锁用于同步线程对共享数据的访问,而条件变量用于在线程之间同步共享数据的值,给多个线程提供了一个会合的场所。
条件变量本身是由互斥量保护的。线程在改变条件状态之前必须先锁住。
接口
#include <pthread.h>/* 动态初始化 */int pthread_cond_init(pthread_cond_t* cond, const pthread_condattr_t* cond_attr);/* 销毁资源*/int pthread_cond_destroy(pthread_cond_t* cond);/* 等待cond 条件发生 */int pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex);/* 通知 cond 条件发生,只通知一个线程 */int pthread_cond_signal(pthread_cond_t* cond);/* 通知cond条件发生,通知所有进程 */int pthread_cond_broadcast(pthread_cond_t* cond);
静态初始化, 设置宏 PTHREAD_COND_INITIALIZER
。
使用实例:基于互斥锁和条件变量单缓冲区(一次只能有一个对象操作)多生产者多消费者模型
有一个链表,限制最多可插入10个结点,
一个互斥锁保护对链表的操作,
两个条件变量通知满 10 以及 为空。
#include <stdio.h>#include <pthread.h>#include <unistd.h>#include <stdlib.h>#include <assert.h>/* 静态初始化 锁 和 条件变量*/pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;pthread_cond_t not_full = PTHREAD_COND_INITIALIZER;pthread_cond_t not_empty = PTHREAD_COND_INITIALIZER;#define MAXSIZE 10int count = 0;typedef struct node{ int data; struct node* next;} node_t, *pnode_t, **ppnode_t;/* 初始化链表的头结点 */void list_init(ppnode_t pphead){ assert(pphead); *pphead = (pnode_t)malloc(sizeof(node_t)); (*pphead)->next = NULL;}/* 头插 */void list_push(pnode_t phead, int data){ assert(phead); pnode_t temp = (pnode_t)malloc(sizeof(node_t)); if(temp){ temp->data = data; temp->next = phead->next; phead->next = temp; }}int list_empty(pnode_t phead){ return phead->next == NULL ? 1 : 0;}/* 头删并通过参数返回被删除的元素 */ void list_pop(pnode_t phead, int* data){ assert(phead); if(!list_empty(phead)) { pnode_t temp = phead->next; phead->next = temp->next; *data = temp->data; free(temp); }}void list_clear(pnode_t phead){ while(!list_empty) { int data; list_pop(phead, &data); }}void list_destory(ppnode_t phead){ assert(phead); list_clear(*phead); free(phead); phead = NULL;}void list_print(pnode_t phead){ pnode_t start = phead->next; while(start) { printf("%d ", start->data); start = start->next; } printf("\n");}void* producer(void* arg){ pnode_t phead = (pnode_t)arg; while(1) { pthread_mutex_lock(&mutex); while(count == MAXSIZE) { pthread_cond_wait(¬_full ,&mutex); } int data = 0; data = rand()%100; list_push(phead,data); printf("producer : %d\n", data); count++; pthread_cond_signal(¬_empty); pthread_mutex_unlock(&mutex); }}void* consumer(void* arg){ pnode_t phead = (pnode_t)arg; while(1) { pthread_mutex_lock(&mutex); while(count == 0) { pthread_cond_wait(¬_empty, &mutex); } int data = 0; list_pop(phead, &data); printf("consumer: %d \n", data); count--; pthread_cond_signal(¬_full); pthread_mutex_unlock(&mutex); sleep(1); }}int main(){ pnode_t phead; list_init(&phead); //pthread_mutex_init(&mutex, NULL); //pthread_cond_init(&cond, NULL); //pthread_cond_init(&empty, NULL); // /* 创建生产者线程和消费者线程 */ pthread_t id1, id2, id3, id4; pthread_create(&id1, NULL, producer, (void*)phead); pthread_create(&id2, NULL, producer, (void*)phead); pthread_create(&id3, NULL, consumer, (void*)phead); pthread_create(&id4, NULL, consumer, (void*)phead); /* 线程等待回收资源 */ pthread_join(id1, NULL); pthread_join(id2, NULL); pthread_join(id3, NULL); pthread_join(id4, NULL); /* 销毁锁和条件变量资源 */ pthread_mutex_destroy(&mutex); pthread_cond_destroy(¬_empty); pthread_cond_destroy(¬_full); return 0;}
信号量
接口:
#include <semaphore.h>/*初始化信号量, pshared 为0表示在同一进程的线程间同步,value 指定信号量的初始值*/int sem_init(sem_t* sem, int pshared, unsigned int value);/* 释放信号量占有的内核资源 */int sem_destory (sem_t* sem);/* 让信号量-1 相当于 P 操作 申请资源 */int sem_wait (sem_t* sem);/* sem_wait 的非阻塞版本 */int sem_trywait (sem_t* sem);/* 让信号量+1 相当于 V 操作 释放资源*/int sem_post (sem_t* sem);
使用实例:基于环形队列的多生产者多消费者模型:
#include <stdio.h>#include <pthread.h>#include <semaphore.h>/* 环形队列可分配资源数目 */#define RESOURCE 6int queue[RESOURCE];int in = 0;int out = 0;/* 可生产数目信号量 , 初始值为环形队列大小*/sem_t sem_p;/* 可消费数目信号线 , 初始值为 0 */sem_t sem_c;/* 生产者与生产者之间的锁 */pthread_mutex_t mutex_p = PTHREAD_MUTEX_INITIALIZER;/* 消费者与消费者之间的锁 */pthread_mutex_t mutex_c = PTHREAD_MUTEX_INITIALIZER;/* 生产者数目,消费者数目*/#define PRODUCER 5#define CONSUMER 5void* producer(void* arg){ while(1) { pthread_mutex_lock(&mutex_p); sem_wait(&sem_p); printf("producer begin ...\n"); queue[in] = rand()%100; int data = queue[in]; in = (in+1) % RESOURCE; printf("producer %d [done] \n", data); sem_post(&sem_c); pthread_mutex_unlock(&mutex_p); }}void* consumer(void* arg){ while(1) { pthread_mutex_lock(&mutex_c); sem_wait(&sem_c); printf("consumer begin ...\n"); int data = queue[out]; queue[out] = -1; out = (out+1) % RESOURCE; sleep(1); printf("consumer %d [done]\n", data); sem_post(&sem_p); pthread_mutex_unlock(&mutex_c); }}int main(){ sem_init(&sem_p, 0, RESOURCE); sem_init(&sem_c, 0, 0); pthread_mutex_init(&mutex_c, NULL); pthread_mutex_init(&mutex_p, NULL); pthread_t p[10]; pthread_t c[10]; /* 创建线程 */ int i = 0; for(; i < PRODUCER; i++){ pthread_create(&p[i], NULL, producer, NULL); } i = 0; for(; i < CONSUMER; i++){ pthread_create(&c[i], NULL, consumer, NULL); } /* 回收线程 */ i = 0; for(; i < PRODUCER ; i++){ pthread_join(p[i], NULL); } i = 0; for(; i < CONSUMER ; i++){ pthread_join(c[i], NULL); } sem_destroy(&sem_p); sem_destroy(&sem_c); pthread_mutex_destroy(&mutex_p); pthread_mutex_destroy(&mutex_c); return 0;}
扩展
还有读写锁,用来处理读者与写者模型。读者与写者互斥与同步关系,读者之间没有关系,写者之间互斥。
第二部分完
- 【Linux】线程总结:线程同步 -互斥锁,条件变量,信号量实现多生产者多消费者模型
- 线程同步:条件变量实现生产者消费者模型
- 【线程的同步与互斥 (互斥量 条件变量 信号量)】生产者与消费者模型
- 线程同步2 ------ 条件变量实现“生产者-消费者”续
- Linux生产者消费者模型--基于线程条件变量
- 消费者生产者问题---线程互斥锁+条件变量
- 线程 ,生产者. 消费者 互斥锁,条件变量
- windows 使用关键段和条件变量实现的生产者和消费者线程同步
- 线程间同步--互斥锁、条件变量、信号量
- 线程间同步--互斥锁、条件变量、信号量
- Linux线程--生产者消费者模型
- 【Linux】生产者消费者编程实现-线程池+信号量
- 【Linux】生产者消费者编程实现-线程池+信号量
- linux 线程间使用信号量实现消费者生产者
- 线程控制[pthread_create() pthread_join()] 线程同步[互斥锁 条件变量 信号量]
- 线程控制[pthread_create() pthread_join()] 线程同步[互斥锁 条件变量 信号量]
- 线程同步之经典生产者-消费者模型
- 3.4生产者/消费者的线程同步模型
- C++学习篇——构造函数与析构函数
- java线程 全面详细的讲解
- 输出三位数的水仙花数
- Java 反射机制详解
- linked-list-cycle-ii
- 【Linux】线程总结:线程同步 -互斥锁,条件变量,信号量实现多生产者多消费者模型
- P1993 差分约束
- Eclipse导出jar文件以及JAR File和Runnable JAR File的区别
- c语言系统网络中发送变长结构体数据包
- jdbc连接数据库 The server time zone value '�й���ʱ��' is unrecognized or represents more... 错误
- 面试总结一
- Qt经典出错信息之QApplication: No such file or directory
- 分布式java应用(二)
- 数组补充 及二位数组 排序方法 冒泡