1、互斥锁(互斥量)
线程同步:多个线程同时访问共享数据时,让他有先后顺序,多个线程争抢锁,抢到锁的可以对共享数据进行操作,没有抢到的线程堵塞等待,等待操作的线程解锁
操作函数
pthread_mutex_t // 创建锁
pthread_mutex_init // 初始化
pthread_mutex_lock // 加锁
pthread_mutex_unlock // 解锁
pthread_mutex_destroy //销毁锁
都是成功返回0,失败返回-1
操作流程:
1、创建一把互斥锁
2、初始化互斥锁
3、加锁
4、解锁
5、销毁锁
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
pthread_mutex_t mutex; // 定义一把互斥锁 (是一个整数)
void* fun(void* arg)
{
srand(time(0));
while(1)
{
pthread_mutex_lock(&mutex); // 加锁 锁--
printf("hello ");
sleep(rand()%3);
printf("world\n");
pthread_mutex_unlock(&mutex); // 解锁 锁++
sleep(rand()%3);
}
return NULL;
}
int main()
{
pthread_t tid;
srand(time(NULL));
int ret = pthread_mutex_init(&mutex,NULL); // 锁变成 1
if(ret != 0)
{
fprintf(stderr,"pthread_mutex_init error:%s\n",strerror(ret));
exit(1);
}
pthread_create(&tid,NULL,fun,NULL);
while(1)
{
pthread_mutex_lock(&mutex); // 加锁
printf("Hello ");
sleep(rand()%3);
printf("World\n");
pthread_mutex_unlock(&mutex); // 解锁
sleep(rand()%3);
}
ret = pthread_join(tid,NULL);
if(ret != 0)
{
fprintf(stderr,"pthread_join error:%s\n",strerror(ret));
exit(1);
}
pthread_mutex_destroy(&mutex); // 销毁互斥锁
pthread_exit((void*)0);
}
2、读写锁
1、写独占,读共享(同时来写的优先级高,优先给写锁)
2、只有一把锁
3、 相较于互斥锁(量)而言,当读线程多的时候,提高效率
读写锁函数
pthread_rwlock_t rwlock 读写锁
pthread_rwlock_init 初始化
pthread_rwlock_rdlock 读锁
pthread_rwlock_wrlock 写锁
pthread_rwlock_unlock 解锁
pthread_rwlock_destroy 销毁锁
函数都没有检查返回值,为了突出逻辑,都是成功返回0,失败返回-1
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
pthread_rwlock_t rwlock; // 创建一把读写锁
int var = 100; // 共享数据
void* fun1(void* arg)
{
while (1)
{
pthread_rwlock_rdlock(&rwlock); // 添加读锁
printf("var=%d\n",var);
pthread_rwlock_unlock(&rwlock); // 解锁
sleep(rand()%1);
}
return NULL;
}
void* fun2(void* arg)
{
srand(time(NULL)); // 随机数种子
while (1)
{
pthread_rwlock_wrlock(&rwlock); // 添加写锁
var = rand()%10000+1;
printf("写锁将数据修改成:%d\n",var);
pthread_rwlock_unlock(&rwlock); // 解锁
sleep(rand()%1); // 预留一下时间,让其他线程争抢到锁
}
return NULL;
}
int main()
{
pthread_t tid1,tid2,tid3,tid4;
pthread_rwlock_init(&rwlock,NULL); // 初始化读写锁
pthread_create(&tid1,NULL,fun1,NULL); // 创建线程
pthread_create(&tid2,NULL,fun2,NULL);
pthread_create(&tid3,NULL,fun1,NULL); // 多创建两个读线程
pthread_create(&tid4,NULL,fun1,NULL);
pthread_join(tid1,NULL); // 回收线程
pthread_join(tid2,NULL);
pthread_join(tid3,NULL);
pthread_join(tid4,NULL);
pthread_rwlock_destroy(&rwlock); // 销毁读写锁
pthread_exit((void*)0);
}
3、死锁
是使用锁不恰当导致的现象:
1、对一个锁反复lock当前这个线程,已经进行了一次加锁,后面又调用了一次加锁,因为加锁失败,就会堵塞,在这里等,就会没办法解锁了
2、两个线程,各自持有一把锁,请求另一把
4、条件变量
条件变量函数
本身不是锁,但是通常结合锁来使用,mutex
pthread_cond_t cond; 条件变量
pthread_cond_init 初始化
pthread_cond_destroy函数
pthread_cond_wait 等待
pthread_cond_signal(); 唤醒阻塞在条件变量上的(至少)一个线程
pthread_cond_broadcast(); 唤醒阻塞在条件变量上的所有线程
pthread_cond_wait函数比较复杂:
1、阻塞等待一个条件变量(需要等待pthread_cond_signal函数唤醒)
2、释放已掌握的互斥锁(解锁互斥量)相当于pthread_mutex_unlock(&mutex);
3、当被唤醒,进行加锁
这个函数总共做了三件事
两个线程:
1、模拟消费者
先判断链表中有没有数据,要是没有等待(pthread_cond_wait),等待生产者生产数据,并唤醒我,然后消耗数据,并解锁
2、模拟生产者
先生产一个数据,拿锁(因为要访问共享数据就需要拿锁),将数据挂到链表上(头插法),解锁,然后通知消费者,有数据了
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.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* arg)
{
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("consumer:%lu--%d\n",pthread_self(),mp->num);
free(mp);
sleep(rand()%3);
}
}
void* producer(void* arg)
{
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()%3);
}
}
int main()
{
pthread_t cid,pid,new;
srand(time(NULL));
pthread_create(&pid,NULL,producer,NULL);
pthread_create(&cid,NULL,consumer,NULL);
// pthread_create(&new,NULL,consumer,NULL);
pthread_join(cid,NULL);
pthread_join(pid,NULL);
// pthread_join(new,NULL);
pthread_exit((void*)0);
}
5、信号量
相当于 初始化值为N 的互斥量;(相当于可以同时有N个可以访问)
信号量函数
sem_t sem; 定义类型
int sem_init(sem_t *sem,int pshared,unsigned int value);
参数:
sem:信号量
pshared: 0 用于线程间同步
1 用于进程间同步
value N指定同时访问的线程数
sem_init();
sem_destroy();
sem_wait();一次调用,做一次--操作,当信号的值为0时,再次--就会阻塞(对比pthread_mutex_lock)
sem_post();一次调用,做一次++操作,当信号量的值,N时,再次++就会阻塞,(对比pthread_mutex_unlock)
初始化是相对于有5个格子可以装东西
现在东西的数量是0个
两者有一者为0时,就会堵塞
1、生产者
东西数量进行++,格子数量进行--
2、消费者
东西数据--,格子数进行++
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <semaphore.h>
#define NUM 5
int queque[NUM]; // 全局数组实现环形队列
sem_t blank_number,product_number; // 空格子信号量,产品信号量
void* producer(void* arg)
{
int i=0;
while (1)
{
sem_wait(&blank_number); // 生产者将格子数--,为0则阻塞等待
queque[i] = rand()%1000+1; // 生产一个产品
printf("produce--%d\n",queque[i]);
sem_post(&product_number); // 将产品数++
i = (i+1)%NUM; // 借助下标实现环形
sleep(rand()%1);
}
}
void* comsumer(void* arg)
{
int i=0;
while(1)
{
sem_wait(&product_number); // 产品数--,为0则阻塞等待
printf("consumer--%d\n",queque[i]);
queque[i] = 0;
sem_post(&blank_number); // 消费以后,将空格子数++
i = (i+1)%NUM;
sleep(rand()%3);
}
}
int main()
{
sem_init(&blank_number,0,NUM); // 初始化空格子信号量是5
sem_init(&product_number,0,0); // 产品初始化是0
pthread_t pid,cid;
pthread_create(&pid,NULL,producer,NULL);
pthread_create(&cid,NULL,comsumer,NULL);
pthread_join(cid,NULL);
pthread_join(pid,NULL);
sem_destroy(&blank_number);
sem_destroy(&product_number);
pthread_exit((void*)0);
}