一、死锁
死锁是指两个或者两个以上的线程/进程在执行过程中,由于存在竞争资源或者由于彼此通信而造成的一种阻塞现象,若没有外力协调,他们都无法进行。
产生的必要条件:
1)、互斥使用,当资源被一个线程使用时,别的线程不能使用
2)、不可抢占,资源请求者不能强制从资源占有者手中抢夺资源,资源只能被拥有者让出
3)、请求与保持,即当资源请求者在请求其他的资源的同时保持对原有资源的占有。
4)、循环等待,即存在一个等待队列:P1占有P2的资源,P2占有P3的资源,P3占有P1的资源。这样就形成了一个等待环路。
注意:当上述四个条件都成立的时候,便形成死锁。当然,死锁的情况下如果打破上述任何一个条件,便可让死锁消失。
1、条件变量
和互斥锁搭配使用,实现同步操作
初始化:pthread_cond_init
等待条件变量产生:pthread_cond_wait
产生条件变量:pthread_cond_signal
1.1 初始化
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t * attr);
功能:初始化条件变量
参数:cond:是一个指向结构pthread_cond_t的指针
restrict attr:是一个指向结构pthread_condattr_t的指针,一般设为NULL
返回值:成功:0 失败:非0
int pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t restrict *mutex);
功能:等待条件的产生
参数:restrict cond:要等待的条件
restrict mutex:对应的锁
返回值:成功:0,失败:不为
int pthread_cond_signal(pthread_cond_t *cond);
功能:产生条件变量
参数:cond:条件变量值
返回值:成功:0,失败:非0
补充:
1)当没有条件产生时pthread_cond_wait函数会阻塞,同时会将锁解开;如果等待到条件产生,函数会结束阻塞同时进行上锁。
2)pthread_cond_wait阻塞状态是等待pthread_cond_signal唤醒
3)pthread_cond_signal只能唤醒单个cond_wait,相当于一对一;pthread_cond_broadcast可以唤醒多个cond_wait,相当于一对多
例如:
两个线程,一个线程倒置全局数组中的数,另一个线程遍历数组中数据,每隔1s遍历打印一次。要求:实现倒置一次,打印一次,顺序执行
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
pthread_mutex_t lock;
pthread_cond_t cond, cond1;
int flag = 0; // 标志位
void *swap_handler(void *arg)
{
int t;
while(1)
{
pthread_mutex_lock(&lock);
if(flag == 1)
pthread_cond_wait(&cond1, &lock);
for(int i = 0; i < 5; i++)
{
t = arr[i];
arr[i] = arr[9-i];
arr[9-i] = t;
}
flag = 1;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&lock);
}
return NULL;
}
void *print_handler(void *arg)
{
while (1)
{
pthread_mutex_lock(&lock);
// 等待条件变量
if(flag == 0)
pthread_cond_wait(&cond, &lock);
for(int i = 0; i < 10; i++)
{
printf("%d", arr[i]);
}
printf("\n");
flag = 0;
pthread_cond_signal(&cond1);
pthread_mutex_unlock(&lock);
sleep(1);
}
return NULL;
}
int main(int argc, char const *argv[])
{
pthread_t t1, t2;
if(pthread_create(&t1, NULL, swap_handler, NULL) != 0)
{
perror("create thread1 error");
return -1;
}
if(pthread_create(&t2, NULL, print_handler, NULL) != 0)
{
perror("create thread2 error");
return -1;
}
if(pthread_mutex_init(&lock, NULL) != 0)
{
perror("mutext init error");
return -1;
}
// 初始化条件变量
if(pthread_cond_init(&cond, NULL) != 0)
{
perror("cond init error");
return -1;
}
if(pthread_cond_init(&cond1, NULL) != 0)
{
perror("cond1 init error");
return -1;
}
pthread_join(t1, NULL);
pthread_join(t2, NULL);
return 0;
}
二、进程间通信
1、无名管道
1)只能用于具有近缘关系的进程之间的通信
2)半双工的通信方式,具有固定的读端fd[0]和写端fd[1]
3)管道可以看成是一种特殊的文件,对于它的读写可以使用文件IO如read、write函数.管道是基于文件描述符的通信方式。
4)当一个管道建立时,它会创建两个文件描述符fd[0]和fd[1]。其中fd[0]固定用于读管道,而fd[1]固定用于写管道。
2、函数接口
int pipe(int fd[2])
功能:创建无名管道
参数:文件描述符 fd[0]:读端 fd[1]:写端
返回值:成功 0
失败 -1
3、读写特性
1)当管道中没有数据时,进行读操作会堵塞;管道中没有数据时,将写端关闭,读操作会立刻返回
2)管道中装满(管道大小64k)数据时,进行写操作会堵塞;一旦有4k空间,写继续
3)只有管道的读端存在时,向管道中写数据才有意义,否则会导致管道破裂,向管道中写入数据的进程将会收到来自内核传来的SIGPIPE信号:Broken pipe 错误(管道破裂)