线程及进程间通信

在函数接口后附有示例代码

一.线程

1.实现方式

  • 一个进程空间内部的所有线程共享数据段和堆区,所以全局变量、静态变量、堆区空间都是共享的,可以利用这些空间通信
  • 多线程操作全局变量空间时会引入资源竞争
  • 多线程要避免引入资源竞争可以通过加互斥锁解决
  • 线程间通信最简单的方法:全局变量 + 锁

2.原子操作

  • 不会被CPU任务调度打断的一次最小的操作称为原子操作

3.互斥锁

  • 避免多线程资源竞争,配合资源使用,使用资源前加锁,使用资源结束后解锁
  • 加锁后,无法再次加锁,必须等到解锁后才能继续加锁

4.临界代码与临界区

  • 加锁解锁中间的代码称为临界代码或临界区
  • 临界代码或临界区不可能同时被CPU任务执行

5.互斥锁函数接口

1).pthread_mutex_init

int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);

功能:互斥锁的初始化

参数:

mutex:互斥锁空间首地址

attr:互斥锁的属性,默认传NULL

返回值:

成功返回0

失败返回非0

2).pthread_mutex_destroy

int pthread_mutex_destroy(pthread_mutex_t *mutex);

功能:互斥锁的销毁

参数:mutex:互斥锁空间首地址

3).pthread_mutex_lock

int pthread_mutex_lock(pthread_mutex_t *mutex);

功能:互斥锁加锁

参数:mutex:互斥锁空间首地址

4).pthread_mutex_unlock

int pthread_mutex_unlock(pthread_mutex_t *mutex);

功能:互斥锁解锁

参数:mutex:互斥锁空间首地址

cs 复制代码
#include"head.h"
//互斥锁,异步
pthread_mutex_t lock;
int Num = 0;
int TmpValue1 = 0;
int TmpValue2 = 0;

void *threadfun1(void *arg)
{
    while(1)
    {
        pthread_mutex_lock(&lock);
        TmpValue1 = Num;
        TmpValue2 = Num;
        Num++;
        pthread_mutex_unlock(&lock);
    }
    return NULL;
}

void *threadfun2(void *arg)
{
    while(1)
    {
        pthread_mutex_lock(&lock);
        if (TmpValue1 != TmpValue2)
        {
            printf("TmpValue1 = %d, TmpValue2 = %d\n", TmpValue1, TmpValue2);
        }
        pthread_mutex_unlock(&lock);

    }
    return NULL;
}

int main()
{
    pthread_t tid1;
    pthread_t tid2;
    int a = 1,b = 2;
    pthread_mutex_init(&lock,NULL);
    pthread_create(&tid1,NULL,threadfun1,NULL);
    pthread_create(&tid2,NULL,threadfun2,NULL);

    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    pthread_mutex_destroy(&lock);
    return 0;
}

6.死锁

  • 多任务通信过程中由于加锁导致多个任务均无法向下执行的状态称为死锁状态,简称为死锁
  • 死锁产生的4个必要条件
    • 互斥条件
    • 不可剥夺条件
    • 请求保持
    • 循环等待
  • 解决死锁
    • 用pthread_mutex_trylock替代pthread_mutex_lock,如果无法加锁成功完成异常处理流程,防止程序卡死
    • 加锁顺序保持一致

7.信号量

  • 信号量可以实现多线程间的同步,让多个任务具有先后顺序关系
    • 同步:拥有严格的先后执行的逻辑顺序关系
    • 异步:代码执行流程没有任何关联性
  • 信号量是一个资源,资源可以初始化、销毁、申请和释放
    • 如果资源数 > 0,则申请资源是让资源数-1
    • 如果资源数为0,申请资源时则会阻塞等待,等待有人释放资源,才能申请拿到资源释放不会阻塞,让资源数+1

8.信号量的函数接口

1).sem_init

int sem_init(sem_t *sem, int pshared, unsigned int value);

功能:

对信号量初始化

参数:

sem:信号量空间首地址

pshared:信号量的作用域

0 线程间共享

非0 进程间共享

value:信号量的初始值

返回值:

成功返回0

失败返回非0

2).sem_destroy

int sem_destroy(sem_t *sem);

功能:销毁无名信号量

参数:sem:信号量空间首地址

返回值:

成功返回0

失败返回非0

3).sem_wait

int sem_wait(sem_t *sem);

功能:申请资源,让资源数-1,如果资源数为0,则阻塞等待

参数:sem:信号量空间的首地址

返回值:

成功返回0

失败返回非0

4).sem_post

int sem_post(sem_t *sem);

功能:释放资源,让资源数+1

参数:sem:信号量空间的首地址

返回值:

成功返回0

失败返回非0

cs 复制代码
#include"head.h"
//信号量,同步
char tmpbuff[1024] = {0};
sem_t sem_r;
sem_t sem_w;

void *threadfun1(void *arg)
{
    while(1)
    {
        sem_wait(&sem_w);
        gets(tmpbuff);
        sem_post(&sem_r);
        if (0 == strcmp(tmpbuff,"over"))
        {
            break;
        }
    }
    return NULL;
}

void *threadfun2(void *arg)
{
    while(1)
    {   
        sem_wait(&sem_r);
        if (0 == strcmp(tmpbuff,"over"))
        {
            break;
        }
        printf("tmpbuff = %s\n",tmpbuff);
        sem_post(&sem_w);

    }
    return NULL;
}

int main()
{
    pthread_t tid1;
    pthread_t tid2;
    int a = 1,b = 2;

    sem_init(&sem_w,0,1);
    sem_init(&sem_r,0,0);
    pthread_create(&tid1,NULL,threadfun1,NULL);
    pthread_create(&tid2,NULL,threadfun2,NULL);

    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    sem_destroy(&sem_w);
    sem_destroy(&sem_r);

    return 0;
}

二.进程

1.进程间通信方式

  • 管道
  • 信号
  • 消息队列
  • 共享内存
  • 信号灯
  • 本地域套接字

2.管道

1).无名管道

  • 只能用于具有亲缘关系的进程间通信

2).有名管道

  • 任意进程间的通信

3).函数接口(无名)

pipe

int pipe(int pipefd[2]);

功能:创建一个用于进程间通信的内核缓存区,返回两个用于读写该内核空间的文件描述

参数:

pipefd:数组

pipefd[0]:读文件描述符

pipefd[1]:写文件描述符

返回值:

成功返回0

失败返回-

cs 复制代码
#include"head.h"

char tmpbuff[1024] = {0};

int main()
{
    pid_t pid;
    int pipefd[2] = {0};
    ssize_t nret = 0;

    pipe(pipefd);

    pid = fork();
    if(-1 == pid)
    {
        perror("fail to fork");
        return -1;
    }
    if(0 == pid)
    {
        write(pipefd[1],"hello world",11);
    }
    else if(pid > 0)
    {
        nret = read(pipefd[0],tmpbuff,sizeof(tmpbuff));
        printf("接收到%ld个字节\n内容:%s\n",nret,tmpbuff);
    }
    close(pipefd[0]);
    close(pipefd[1]);
    return 0;
}
相关推荐
莫寒清1 小时前
Apache Tika
java·人工智能·spring·apache·知识图谱
昱宸星光1 小时前
spring cloud gateway内置网关filter
java·服务器·前端
麻瓜生活睁不开眼1 小时前
Android 14 开机自启动第三方 APK 全流程踩坑与最终解决方案(含 RescueParty 避坑)
android·java·深度学习
weixin_395448912 小时前
build_fsd_luyan_from_rm.py-cursor0225
开发语言·python
tod1132 小时前
OS 核心知识点全解析(一)
linux·开发语言·面试经验
当战神遇到编程2 小时前
LinkedList深入讲解
java·intellij-idea
kylezhao20192 小时前
C#中的反射是什么?详细讲解以及在工控上位机中如何应用
java·开发语言
张三_02262 小时前
Java并发:我用修仙小说讲AQS
java
what丶k2 小时前
【微服务】Spring AI 使用详解:让微服务无缝集成 AI 能力
java·后端·ai编程