信号变量
条件本身不是锁!但它也可以造成线程阻塞。通常与互斥锁配合使用。给多线程提供一个会合的场所。
使用互斥量保护共享数据
使用条件变量可以使线程阻塞, 等待某个条件的发生, 当条件满足的时候解除阻塞
条件变量的两个动作:
条件不满足,线程阻塞
条件满足,通知阻塞的线程解除阻塞, 开始工作。
条件变量相关函数
pthread_cond_t cond;
定义一个条件变量
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
函数描述:
初始化条件变量
函数参数
cond:条件变量
attr:条件变量属性, 通常传NULL
函数返回值:成功返回0,错误返回错误号
int pthread_cond_destroy(pthread_cond_t *cond);
销毁条件变量
int pthread_cond_wait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex);
函数描述:
条件不满足,引起线程阻塞并解锁
条件满足,解除线程阻塞,并加锁
函数参数:
cond: 条件变量
mutex: 互斥锁变量
函数返回值:成功返回0,错误返回错误号
int pthread_cond_signal(pthread_cond_t *cond);
函数描述:
唤醒至少一个阻塞在该条件变量上的线程
函数参数:
条件变量–>cond
函数返回值: 成功返回0, 失败返回错误号
//使用条件变量实现生产者和消费者模型
/*
用链表模仿生产者和消费者
生产者:向链表中插入节点
消费者:从链表中读取节点,同时将该节点销毁
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
typedef struct node
{
int data;
struct node *next;
}NODE;
NODE *head = NULL;
//定义一把锁
pthread_mutex_t mutex;
//定义条件变量
pthread_cond_t cond;
//生产者线程
void *producer(void *arg)
{
NODE *pNode = NULL;
while(1)
{
//生产一个节点
pNode = (NODE *)malloc(sizeof(NODE));
if(pNode==NULL)
{
perror("malloc error");
exit(-1);
}
pNode->data = rand()%1000;
printf("P:[%d]\n", pNode->data);
//加锁
pthread_mutex_lock(&mutex);
pNode->next = head;
head = pNode;
//解锁
pthread_mutex_unlock(&mutex);
//通知消费者线程解除阻塞
pthread_cond_signal(&cond);
sleep(rand()%3);
}
}
//消费者线程
void *consumer(void *arg)
{
NODE *pNode = NULL;
while(1)
{
//加锁
pthread_mutex_lock(&mutex);
if(head==NULL)
{
//若条件不满足,需要阻塞等待
//若条件不满足,则阻塞等待并解锁;
//若条件满足(被生成者线程调用pthread_cond_signal函数通知),解除阻塞并加锁
pthread_cond_wait(&cond, &mutex);
}
printf("C:[%d]\n", head->data);
pNode = head;
head = head->next;
//解锁
pthread_mutex_unlock(&mutex);
free(pNode);
pNode = NULL;
sleep(rand()%3);
}
}
int main()
{
int ret;
pthread_t thread1;
pthread_t thread2;
//初始化互斥锁
pthread_mutex_init(&mutex, NULL);
//条件变量初始化
pthread_cond_init(&cond, NULL);
//创建生产者线程
ret = pthread_create(&thread1, NULL, producer, NULL);
if(ret!=0)
{
printf("pthread_create error, [%s]\n", strerror(ret));
return -1;
}
//创建消费者线程
ret = pthread_create(&thread2, NULL, consumer, NULL);
if(ret!=0)
{
printf("pthread_create error, [%s]\n", strerror(ret));
return -1;
}
//等待线程结束
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
//释放互斥锁
pthread_mutex_destroy(&mutex);
//释放条件变量
pthread_cond_destroy(&cond);
return 0;
}
多个生成者和多个消费者程序在执行的时候core掉的原因分析:
假若只有一个生产者生产了一个节点, 此时会调用pthread_cond_signal通知
消费者线程, 此时若有多个消费者被唤醒了, 则最终只有消费者获得锁, 然后进行
消费, 此时会将head置为NULL, 然后其余的几个消费者线程只会有一个线程获得锁,
然后读取head的内容就会core掉.
在使用条件变量的线程中, 能够引起线程的阻塞的地方有两个:
1 在条件变量处引起阻塞---->这个阻塞会被pthread_cond_signal解除阻塞
2 互斥锁也会使线程引起阻塞----->其他线程解锁会使该线程解除阻塞.
读写锁使用步骤:
1 先定义一把读写锁:
pthread_rwlock_t rwlock;
2 初始化读写锁
pthread_rwlock_init(&rwlock, NULL);
3 加锁
pthread_rwlock_rdlock(&rwlock);---->加读锁
pthread_rwlock_wrlock(&rwlock);---->加写锁
共享资源出现的位置
/
4 解锁
pthread_rwlock_unlock(&rwlock);
5 释放锁
pthread_rwlock_destroy(&rwlock);
信号量
信号量相当于多把锁, 可以理解为是加强版的互斥锁
相关函数
- 定义信号量 sem_t sem;
- int sem_init(sem_t *sem, int pshared, unsigned int value);
函数描述: 初始化信号量
函数参数:
sem: 信号量变量
pshared: 0表示线程同步, 1表示进程同步
value: 最多有几个线程操作共享数据
函数返回值:成功返回0, 失败返回-1, 并设置errno值 - int sem_wait(sem_t *sem);
函数描述: 调用该函数一次, 相当于sem–, 当sem为0的时候, 引起阻塞
函数参数: 信号量变量
函数返回值: 成功返回0, 失败返回-1, 并设置errno值 - int sem_post(sem_t *sem);
函数描述: 调用一次, 相当于sem++
函数参数: 信号量变量
函数返回值: 成功返回0, 失败返回-1, 并设置errno值 - int sem_trywait(sem_t *sem);
函数描述: 尝试加锁, 若失败直接返回, 不阻塞
函数参数: 信号量变量
函数返回值: 成功返回0, 失败返回-1, 并设置errno值 - int sem_destroy(sem_t *sem);
函数描述: 销毁信号量
函数参数: 信号量变量
函数返回值: 成功返回0, 失败返回-1, 并设置errno值
信号量实现步骤:
1 定义信号量变量
sem_t sem1;
sem_t sem2;
2 初始化信号量
sem_init(&sem1, 0, 5);
sem_init(&sem2, 0, 5);
3 加锁
sem_wait(&sem1);
//共享资源
sem_post(&sem2);
-------------------------------------
sem_wait(&sem2);
//共享资源
sem_post(&sem1);
4 释放资源
sem_destroy(sem1);
sem_destroy(sem2);
//使用信号量实现生产者和消费者模型
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
typedef struct node
{
int data;
struct node *next;
}NODE;
NODE *head = NULL;
//定义信号量
sem_t sem_producer;
sem_t sem_consumer;
//生产者线程
void *producer(void *arg)
{
NODE *pNode = NULL;
while(1)
{
//生产一个节点
pNode = (NODE *)malloc(sizeof(NODE));
if(pNode==NULL)
{
perror("malloc error");
exit(-1);
}
pNode->data = rand()%1000;
printf("P:[%d]\n", pNode->data);
//加锁
sem_wait(&sem_producer); //--
pNode->next = head;
head = pNode;
//解锁
sem_post(&sem_consumer); //相当于++
sleep(rand()%3);
}
}
//消费者线程
void *consumer(void *arg)
{
NODE *pNode = NULL;
while(1)
{
//加锁
sem_wait(&sem_consumer); //相当于--
printf("C:[%d]\n", head->data);
pNode = head;
head = head->next;
//解锁
sem_post(&sem_producer); //相当于++
free(pNode);
pNode = NULL;
sleep(rand()%3);
}
}
int main()
{
int ret;
pthread_t thread1;
pthread_t thread2;
//初始化信号量
sem_init(&sem_producer, 0, 5);
sem_init(&sem_consumer, 0, 0);
//创建生产者线程
ret = pthread_create(&thread1, NULL, producer, NULL);
if(ret!=0)
{
printf("pthread_create error, [%s]\n", strerror(ret));
return -1;
}
//创建消费者线程
ret = pthread_create(&thread2, NULL, consumer, NULL);
if(ret!=0)
{
printf("pthread_create error, [%s]\n", strerror(ret));
return -1;
}
//等待线程结束
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
//释放信号量资源
sem_destroy(&sem_producer);
sem_destroy(&sem_consumer);
return 0;
}