IO进程问题之死锁与进程间通信

一、死锁

        死锁是指两个或者两个以上的线程/进程在执行过程中,由于存在竞争资源或者由于彼此通信而造成的一种阻塞现象,若没有外力协调,他们都无法进行。

        产生的必要条件:

        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 错误(管道破裂)

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值