死锁
多个线程之间使用互斥锁,每把互斥锁都处于上锁态,每把互斥锁又都需要另外的线程运行后才能解锁。
死锁的解决方式
pthread_mutex_trylock函数
函数原型:
int pthread_mutex_trylock(pthread_mutex_t *mutex);
功能:
尝试上锁互斥锁,如果该互斥锁已经上锁,则尝试上锁失败
返回值:
成功上锁返回0,失败返回错误码
互斥锁的属性
快速锁:
默认的互斥锁(第一次调用lock不会阻塞,第二次调用就会阻塞,第三次开始调用lock就会死锁)
递归锁:
第一次调用lock不会阻塞,第二次调用就会阻塞,第三次调用lock开始,返回0,并在互斥锁内部记录lock次数。(当互斥锁解锁时,只有解锁与lock次数相同的次数,才能解锁成功)
错误检查锁:
第一次调用lock不会阻塞,第二次调用就会阻塞,第三次调用lock开始,每次调用lock都会返回错误码
递归锁和错误检查锁都是为了防止死锁,只是应用场景不一样(快速锁可以通过trylock函数实现防止死锁)
递归锁的创建方式
静态创建
pthread_mutex_t recmutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
动态创建:
- 创建一个互斥锁属性变量
pthread_mutexattr_t attr;
- 初始化一个互斥锁属性变量
pthread_mutexattr_init (&attr);
- 及那个互斥锁的属性设置为递归锁
pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE_NP);
- 初始化互斥锁
pthread_mutex_init(&mutex,&attr);
错误检查锁的创建方式
静态创建
pthread_mutex_terrchkmutex=PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
动态创建:
- 创建一个互斥锁属性变量
pthread_mutexattr_t attr;
- 初始化一个互斥锁属性变量
pthread_mutexattr_init (&attr);
- 及那个互斥锁的属性设置为错误检查锁
pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_ERRORCHECK_NP);
- 初始化互斥锁
pthread_mutex_init(&mutex,&attr);
无名信号量
无名信号量与互斥锁工作方式90%相同
区别:
无名信号量的值为0~n
互斥锁的值为0~1
互斥锁第二次上锁就会阻塞
无名信号量,根据信号量的值来决定第几次上锁才会阻塞
无名信号量的操作流程
- 创建无名信号量变量
sem_t sem;
- 初始化无名信号量
函数原型:
int sem_init(sem_t *sem, int pshared, unsigned int value);
调用形式:
sem_init(&sem,0,0/1/2/3/4/.....)
功能:
初始化无名信号量sem,并且设置初始值为value
参数 sem:等待初始化的无名信号量
参数 pshared:
0:表示在一个进程的多线程间共享,此时要求无名信号量所处的内存能够被所有线程访问
非0:表示在多个进程间共享,此时要求无名信号量申请在共享内存区域
参数value:无名信号量的初始值
仅当无名信号量的value为0时,再次上锁,无名信号量才会产生阻塞
- 获取/释放无名信号量(上锁/解锁)
获取无名信号量的value-1
函数原型:
int sem_wait(sem_t *sem);
调用形式:
sem_wait(&sem) ;
功能:
获取信号量中的一个资源,即信号量的value-1,如果当前信号量的value==0的话,则wait函数阻塞
函数原型:
int sem_trywait(sem_t *sem);
调用形式:
sem_trywait(&sem);
功能:
尝试获取信号量中的一个资源,如果当前信号量的value==0的话,则尝试失败,返回-1,获取成功,返回0
*没用递归信号量和错误检查信号量,trywait是防止死锁的唯一手段
释放无名信号量的value+1
函数原型:
int sem_post(sem_t *sem);
调用形式:
sem_post(&sem);
功能:
释放信号量中的一个资源,即信号量的value+1(可以一直加,超过初始值也可以)
无名信号量:互斥
无名信号量:同步
条件变量
在多线程中,只负责同步,不负责互斥
什么是条件变量
在多线程中,专门负责用来判断,当前线程是否满足运行条件
工作逻辑为:
线程1:无论是否满足运行条件,总是询问是否满足条件,只要询问了,就会阻塞(因为要等回复)
线程1只负责问,问谁无所谓
线程2:仅当线程1满足运行条件的情况下,才应该回复线程1,你满足条件了
场景举例
线程1:每次消费3个苹果
线程2:每次生产1个苹果
线程1每次消费之前,都会提问是否拥有3个苹果,只要问了就阻塞,哪怕现在有3个苹果也要问,问了也会阻塞
线程2每次生产完苹果之后,判断当前苹果数量是否达到了3个,如果达到了3个,就会通知线程1,允许消费苹果。此时线程1就会解除阻塞,正常消费苹果
条件变量的创建流程
创建条件变量和互斥锁
pthread_cond_t cond
pthread_mutex_t mutex
初始化条件变量和互斥锁
函数原型:
int pthread_cond_init(pthread_cond_t* cond,pthread_condattr_t*cond_attr);
调用形式:
pthread_cond_init(&cond,0);
功能描述:初始化条件变量
参数 cond:等待初始化的条件变量的地址
参数 cond_attr:这里只能传0,表示默认属性
pthread_mutex_init(&mutex,0)
条件询问,产生阻塞
函数原型:
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
调用形式:
pthread_cond_wait(&cond,&mutex)
功能:询问条件是否满足,阻塞等待回复
以为条件变量不负责保护临界资源,所以一定要互斥锁来保护临界资源的安全
wait会自动解锁互斥锁以避免死锁。
回答询问,条件唤醒,解除阻塞
函数原型:
int pthread_cond_signal(pthread_cond_t *cond);
调用形式:
pthread_cond_signal(&cond)
功能描述:回答wait的问题,唤醒因为wait而产生阻塞的线程
如果多个线程都以为wait而阻塞,一个signal只能唤醒一个wait
此时多个wait会抢夺一个signal,谁抢到谁运行
函数原型:
int pthread_cond_broadcast(pthread_cond_t *cond);
调用形式:
pthread_cond_broadcast(&cond)
功能:唤醒当前线程中,所有被wait阻塞的线程,先wait的优先唤醒
被唤醒的wait会自动锁上那把互斥锁
当不再使用条件变量时,销毁条件变量
函数原型:
int pthread_cond_destroy(pthread_cond_t *cond);
调用形式:
pthread_cond_destroy(&cond)
功能:销毁条件变量
练习:
有两个线程
1#线程负责每秒生产一个苹果
2#线程负责每秒消费3个苹果
要求使用条件变量使得用户买到正常数量苹果(苹果不会越来越多,也不会变为负数)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <wait.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/un.h>
typedef struct sockaddr_in addr_in_t;
typedef struct sockaddr addr_t;
typedef struct sockaddr_un addr_un_t;
pthread_cond_t c;
pthread_mutex_t m;
int a=0;
void* task1(void* arg)
{
while(1)
{
pthread_mutex_lock(&m);
pthread_cond_wait(&c,&m);
a-=3;
printf("顾客买了3个苹果,还剩%d个苹果\n",a);
sleep(1);
pthread_mutex_unlock(&m);
}
}
int main(int argc, const char *argv[])
{
pthread_cond_init(&c,0);
pthread_mutex_init(&m,0);
pthread_t id1;
pthread_create(&id1,0,task1,0);
pthread_detach(id1);
int i;
while(1)
{
pthread_mutex_lock(&m);
a++;
printf("有%d个苹果\n",a);
if(a>=3)
{
pthread_cond_signal(&c);
}
else
{
printf("不满足客服购买需求\n");
}
pthread_mutex_unlock(&m);
sleep(1);
}
return 0;
}
有三个线程
1#线程负责每秒消费3个苹果
2#线程负责每2秒消费5个橘子
3#线程每秒生成1个苹果或2个橘子
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <wait.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/un.h>
typedef struct sockaddr_in addr_in_t;
typedef struct sockaddr addr_t;
typedef struct sockaddr_un addr_un_t;
pthread_cond_t c;
pthread_cond_t d;
pthread_mutex_t m;
int a=0;
int b=0;
int sec=0;
void* task1(void* arg)
{
while(1)
{
pthread_mutex_lock(&m);
pthread_cond_wait(&c,&m);
a-=3;
printf("顾客买了3个苹果,还剩%d个苹果\n",a);
sleep(1);
pthread_mutex_unlock(&m);
}
}
void* task2(void* arg)
{
while(1)
{
pthread_mutex_lock(&m);
pthread_cond_wait(&d,&m);
b-=5;
printf("顾客买了5个橘子,还剩%d个橘子\n",b);
sleep(1);
pthread_mutex_unlock(&m);
}
}
int main(int argc, const char *argv[])
{
srand(time(0));
pthread_cond_init(&c,0);
pthread_cond_init(&d,0);
pthread_mutex_init(&m,0);
pthread_t id1,id2;
pthread_create(&id1,0,task1,0);
pthread_create(&id2,0,task2,0);
pthread_detach(id1);
int i;
while(1)
{
int i=rand()%2;
pthread_mutex_lock(&m);
sec++;
if(0==i)
{
a++;
printf("有%d个苹果\n",a);
if(a>=3)
{
pthread_cond_broadcast(&c);
}
else
{
printf("不满足客服购买需求\n");
}
}
else
{
b+=2;
printf("有%d个橘子\n",b);
if(b>=5&&sec%2==0)
{
pthread_cond_broadcast(&d);
}
else
{
printf("不满足客服购买需求\n");
}
}
pthread_mutex_unlock(&m);
sleep(1);
}
return 0;
}
time函数
原型:time_t time(time_t *tloc);
调用形式:
1: time_t tim = time(0)
2: time_t tim;
time(&tim);
功能描述:
time会返回从1970年0:0:0 到调用time函数的时候,所经过的秒数
如果参数 tloc 不是 NULL,则返回时会被同步的保存到 tloc指向的内存中
localtime函数
原型:struct tm *localtime(const time_t *timep);
调用形式:
time_t tim = time(0)
struct tm* lt = localtime(&tim)
功能描述:
将从1970.1.1 0:0:0 到现在经过的秒数,传入localtime函数里面,该函数会自动的把这个时间戳转换成当前的年月日,时分秒 等数据,这些数据会以返回值的形式返回
struct tm {
int tm_sec; /* Seconds (0-60) */
int tm_min; /* Minutes (0-59) */
int tm_hour; /* Hours (0-23) */
int tm_mday; /* Day of the month (1-31) */
int tm_mon; /* Month (0-11) */ 1月份是0,12月份是11
int tm_year; /* Year - 1900 */ 计算出来的年份,是从1900年开始算的,如果今年是2000年,那么 tm_year的值就是 100
int tm_wday; /* Day of the week (0-6, Sunday = 0) */
int tm_yday; /* Day in the year (0-365, 1 Jan = 0) */
int tm_isdst; /* Daylight saving time */
};
练习:
编写代码验证递归锁和错误检查锁
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <wait.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/un.h>
typedef struct sockaddr_in addr_in_t;
typedef struct sockaddr addr_t;
typedef struct sockaddr_un addr_un_t;
pthread_mutex_t ma;
pthread_mutex_t mb;
int count=-1;
/*
void* task(void* arg)
{
char b;
scanf("%c",&b);
if(b==' ')
{
pthread_mutex_unlock(&ma);
}
}
*/
int main(int argc, const char *argv[])
{
pthread_mutexattr_t attr,attr1;
pthread_mutexattr_init (&attr);
pthread_mutexattr_init (&attr1);
pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE_NP);
pthread_mutexattr_settype(&attr1,PTHREAD_MUTEX_ERRORCHECK_NP);
pthread_mutex_init(&ma,&attr); //创建递归锁
pthread_mutex_init(&mb,&attr1); //创建错误信息锁
pthread_t id;
pthread_create(&id,0,task,0);
pthread_detach(id);
//递归锁
while(1)
{
count++;
printf("lock次数=%d\n",count);
int a=pthread_mutex_lock(&ma);
printf("A\n");
printf("返回值=%d\n",a);
sleep(1);
}
/*
//错误信息锁
while(1)
{
int a=pthread_mutex_lock(&mb);
printf("A\n");
printf("返回值=%d\n",a);
sleep(1);
}
*/
return 0;
}
2个生产者线程
1#每秒生产1个苹果
2#每秒生成2个橘子
2个消费者
1#每秒消费3个苹果
2#每两秒消费5个橘子
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <wait.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/un.h>
typedef struct sockaddr_in addr_in_t;
typedef struct sockaddr addr_t;
typedef struct sockaddr_un addr_un_t;
pthread_cond_t c;
pthread_cond_t d;
pthread_mutex_t m;
pthread_mutex_t n;
int a=0;
int b=0;
int sec=0;
void* task0(void* arg)
{
while(1)
{
pthread_mutex_lock(&n);
b+=2;
printf("有%d个橘子\n",b);
if(b>=5&&sec%2==0)
{
pthread_cond_broadcast(&d);
}
else
{
printf("不满足客服购买需求\n");
}
pthread_mutex_unlock(&m);
}
}
void* task1(void* arg)
{
while(1)
{
pthread_mutex_lock(&m);
pthread_cond_wait(&c,&m);
a-=3;
printf("顾客买了3个苹果,还剩%d个苹果\n",a);
pthread_mutex_unlock(&m);
}
}
void* task2(void* arg)
{
while(1)
{
pthread_mutex_lock(&n);
pthread_cond_wait(&d,&n);
b-=5;
printf("顾客买了5个橘子,还剩%d个橘子\n",b);
sleep(1);
pthread_mutex_unlock(&n);
}
}
int main(int argc, const char *argv[])
{
srand(time(0));
pthread_cond_init(&c,0);
pthread_cond_init(&d,0);
pthread_mutex_init(&m,0);
pthread_mutex_init(&n,0);
pthread_mutex_lock(&n);
pthread_t id0,id1,id2;
pthread_create(&id0,0,task0,0);
pthread_create(&id1,0,task1,0);
pthread_create(&id2,0,task2,0);
pthread_detach(id1);
int i;
while(1)
{
pthread_mutex_lock(&m);
sec++;
printf("%d秒\n",sec);
a++;
printf("有%d个苹果\n",a);
if(a>=3)
{
pthread_cond_broadcast(&c);
}
else
{
printf("不满足客服购买需求\n");
}
pthread_mutex_unlock(&n);
sleep(1);
}
return 0;
}
2个生产者线程
1#每秒生产1个苹果
2#每秒生成2个橘子
2个消费者
1#每秒消费3个苹果
2#每两秒消费5个橘子
由于仓库有限,生产了橘子之后就不能生成苹果,反之一样。
由于仓库有限,仓库最多存放10个苹果,或者最多20个橘子
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <wait.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/un.h>
typedef struct sockaddr_in addr_in_t;
typedef struct sockaddr addr_t;
typedef struct sockaddr_un addr_un_t;
pthread_cond_t c;
pthread_cond_t d;
pthread_cond_t e;
pthread_cond_t f;
pthread_mutex_t m;
pthread_mutex_t n;
pthread_mutex_t i;
int a=0;
int b=0;
int sec=0;
int k=0;
int idex;
void* task(void*arg)
{
while(1)
{
pthread_mutex_lock(&m);
pthread_cond_wait(&f,&m);
sec++;
printf("第%d秒生产苹果\n",sec);
if(a<10)
{
a++;
}
printf("有%d个苹果\n",a);
if(a>=3)
{
pthread_cond_broadcast(&c);
}
else
{
printf("不满足客服购买需求\n");
}
pthread_mutex_unlock(&m);
//pthread_mutex_unlock(&i);
sleep(1);
}
}
void* task0(void* arg)
{
while(1)
{
pthread_mutex_lock(&n);
pthread_cond_wait(&e,&n); //询问是否生产苹果
sec++;
printf("第%d秒生产橘子\n",sec);
if(b<20)
{
b+=2;
}
printf("有%d个橘子\n",b);
if(b>=5&&sec%2==0)
{
pthread_cond_broadcast(&d);
}
else
{
printf("不满足客服购买需求\n");
}
pthread_mutex_unlock(&n);
//pthread_mutex_unlock(&i);
sleep(1);
}
}
void* task1(void* arg)
{
while(1)
{
pthread_mutex_lock(&m);
pthread_cond_wait(&c,&m);
a-=3;
printf("顾客买了3个苹果,还剩%d个苹果\n",a);
k=a;
if(0==k)
{
idex=rand()%2; //判断生产苹果还是生产橘子(0生产苹果,1生产橘子)
printf("idex=%d\n",idex);
pthread_mutex_unlock(&i);
}
else
{
pthread_mutex_unlock(&m);
}
sleep(1);
}
}
void* task2(void* arg)
{
while(1)
{
pthread_mutex_lock(&n);
pthread_cond_wait(&d,&n);
b-=5;
printf("顾客买了5个橘子,还剩%d个橘子\n",b);
k=b;
if(0==k)
{
idex=rand()%2; //判断生产苹果还是生产橘子(0生产苹果,1生产橘子)
printf("idex=%d\n",idex);
pthread_mutex_unlock(&i);
}
else
{
pthread_mutex_unlock(&n);
}
sleep(1);
}
}
int main(int argc, const char *argv[])
{
srand(time(0));
pthread_cond_init(&c,0);
pthread_cond_init(&d,0);
pthread_cond_init(&e,0);
pthread_cond_init(&f,0);
pthread_mutex_init(&m,0);
pthread_mutex_init(&n,0);
pthread_mutex_init(&i,0);
pthread_t id,id0,id1,id2;
pthread_create(&id,0,task,0);
pthread_create(&id0,0,task0,0);
pthread_create(&id1,0,task1,0);
pthread_create(&id2,0,task2,0);
pthread_detach(id);
pthread_detach(id0);
pthread_detach(id1);
pthread_detach(id2);
idex=rand()%2; //判断生产苹果还是生产橘子(0生产苹果,1生产橘子)
printf("idex初始值=%d\n",idex);
while(1)
{
pthread_mutex_trylock(&i);
if(0==idex && b==0)
{
pthread_mutex_trylock(&n);
pthread_cond_broadcast(&f);
pthread_mutex_unlock(&m);
}
else if(1==idex && a==0)
{
pthread_mutex_trylock(&m);
pthread_cond_broadcast(&e);
pthread_mutex_unlock(&n);
}
}
return 0;
}