【DAY25】线程与进程通信:共享内存、同步机制及实现方案

一、线程间通信

1.实现方式

  1. 一个进程空间内部的所有线程共享数据段和堆区,所以全局变量、静态变量、堆区空间都是共享的,可以利用这些空间通信

  2. 多线程操作全局变量空间时会引入资源竞争

  3. 多线程要避免引入资源竞争可以通过加互斥锁解决

  4. 线程间通信最简单的方法:全局变量 + 锁

2.原子操作

不会被CPU任务调度打断的一次最小的操作

3.互斥锁

1.避免多线程资源竞争,配合资源使用,使用资源前加锁,使用资源结束后解锁

2.加锁后,无法再次加锁,必须等到解锁后才能继续加锁

4.临界代码与临界区

  1. 加锁解锁中间的代码称为临界代码或临界区

  2. 临界代码或临界区不可能同时被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_destory

int pthread_mutex_destroy(pthread_mutex_t *mutex);

功能:

互斥锁的销毁

参数:

mutex:互斥锁空间首地址

返回值:

成功返回0

失败返回非0

3.pthread_mutex_lock

int pthread_mutex_lock(pthread_mutex_t *mutex);

功能:

互斥锁加锁

参数:

mutex:互斥锁空间首地址

返回值:

成功返回0

失败返回非0

4.pthread_mutex_unlock

int pthread_mutex_unlock(pthread_mutex_t *mutex)

功能:互斥锁解锁

参数:

mutex:互斥锁空间首地址

返回值:

成功返回0

失败返回非0

6.死锁

多任务通信过程中由于枷锁导致多个人物均无法向下执行的状态

1.死锁产生的必要条件

互斥条件

不可剥夺条件

请求保持

循环等待

2.解决死锁

1>用pthread_mutex_tylock 替代 pthread_mutex_lock

2>加锁顺序保持一致

7.信号量

同步所有线程,让多个任务执行有先后顺序

1)同步:拥有严格的先后执行的逻辑顺序关系

2)异步:代码执行流程没有任何关联

3)信号量是一个资源,资源可以初始化、销毁、申请、释放

  • 若资源数为0,申请资源时会阻塞等待,等待释放资源后,才能拿到资源
  • 若资源数 >0 ,则申请资源,资源数-1
  • 释放不会阻塞, 资源数+1

8.信号量函数接口

1. sem_init

int set_init (set_t *sem, int pshared, unsigned int value);

功能:

初始化信号量

参数:

  • sem: 信号空间首地址
  • pshared: 信号的作用域

0, 线程间

非0, 进程间

  • valu:信号量的初始值

返回值:

成功返回0; 失败返回非0值

2.sem_destroy

int sem_destroy(sem_t *sem)

功能:

初始化信号量

参数:

  • sem: 信号空间首地址
  • pshared: 信号的作用域

0, 线程间

非0, 进程间

  • valu:信号量的初始值

返回值:

成功返回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

练习:创建3个线程任务,线程任务分别打印A B C

  1. 线程1循环打印A

  2. 线程2循环打印B

  3. 线程3循环打印C

要求三个线程打印出来的顺序总为A B C

cpp 复制代码
#include<stdio.h>
#include<string.h>
#include<pthread.h>
#include<semaphore.h>

char tmp[4096] = {0};
sem_t w;
sem_t r;
sem_t p;

void* thread1 (void *arg)
{
    while(1)
    {
        sem_wait(&w);
        printf("A");
        sem_post(&p);
    }
    return NULL;
}

void* thread2 (void *arg)
{
    while(1)
    {
        sem_wait(&p);
        printf("B");
        sem_post(&r);
    }

    return NULL;
}

void* thread3 (void *arg)
{
    while(1)
    {
        sem_wait(&r);
        printf("C\n");
        sem_post(&w);
    }

    return NULL;
}
int main(void)
{
    pthread_t tid1;
    pthread_t tid2;
    pthread_t tid3;

    sem_init(&w, 0, 1);
    sem_init(&r, 0, 0);
    sem_init(&p, 0, 0);

    pthread_create(&tid1, NULL, thread1, NULL);
    pthread_create(&tid2, NULL, thread2, NULL);
    pthread_create(&tid3, NULL, thread3, NULL);

    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    pthread_join(tid3, NULL);

    sem_destroy(&r);
    sem_destroy(&w);
    sem_destroy(&p);

    return 0;
}

二、进程间通信

1.进程间通信的方式

通过内核通信

1.管道(最简单、最方便)

2.信号

3.消息队列

4.共享内存(最高效)

5.信号灯

6.本地域套接字

2.管道

1.无名管道

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

2.有名管道

  • 任意进程间的通信

3.函数接口

1.pipe

int pipe(int pipefd[2]);

功能:

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

参数:

pipefd:数组

pipefd[0] : 读文件描述符

pipefd[1] : 写文件描述符

返回值:

成功返回0

失败返回-1

相关推荐
爱编码的小八嘎8 小时前
C‘语言完美演绎9-11
c语言
坚持就完事了8 小时前
YARN资源管理器
大数据·linux·hadoop·学习
Joseph Cooper9 小时前
Linux regmap 子系统实战:在驱动中 dump PMIC 寄存器定位供电问题
linux·运维·服务器
一行代码一行诗++9 小时前
C语言中if的使用
c语言·c++·算法
来生硬件工程师9 小时前
【程序库】 MutiButton 按键库
c语言·笔记·stm32·单片机·mcu·嵌入式实时数据库
计算机安禾9 小时前
【Linux从入门到精通】第35篇:容器化技术预备——Docker安装与基本概念
linux·运维·docker
子木HAPPY阳VIP9 小时前
信创UOS,Docker 完整操作部署(Dockerfile部署方式)&排错整合
linux·运维·redis·nginx·docker·容器·tomcat
瞎折腾啥啊9 小时前
vcpkg与CMake
linux·c++·cmake·cmakelists
AOwhisky9 小时前
Kubernetes调度与服务暴露:从“定时任务”到“服务发现”的完全指南
linux·运维·云原生·容器·kubernetes·服务发现
wljy19 小时前
牛客每日一题(2026.4.30) 整数域二分
c语言·c++·算法·蓝桥杯·二分