当前位置: 首页 > news >正文

Linux软件编程(七)线程间同步与进程间通信

一、线程间同步机制

定义:让多个线程在执行某个任务时,具有先后顺序的执行。

异步定义:多线程、多进程本质是异步执行,CPU 调度是随机的。

1. 信号量(Semaphore)

用于实现线程间同步。

操作步骤

  1. 定义信号量对象

  2. 初始化信号量

  3. PV 操作:P(申请信号量);V(释放信号量)

  4. 销毁信号量

相关函数接口

#include <semaphore.h>

sem_t sem; // 定义对象
int sem_init(sem_t *sem, int pshared, unsigned int value); // 初始化信号量
int sem_wait(sem_t *sem);  // P操作:申请信号量
int sem_post(sem_t *sem);  // V操作:释放信号量
int sem_destroy(sem_t *sem); // 销毁信号量

参数说明

  • pshared = 0:线程间共享

  • pshared != 0:进程间共享

  • value:信号量初始值


2. 死锁及解决方法

死锁:在多线程环境中,每个线程持有资源并请求其他线程持有的资源,导致无限等待。

产生死锁的四个必要条件

  1. 互斥条件:资源每次只能被一个线程使用。

  2. 请求与保持条件:线程请求新资源时不释放已持有的资源。

  3. 不剥夺条件:资源无法被强行剥夺。

  4. 循环等待条件:多个线程形成环状等待资源。

解决方法

  1. 锁一定要成对出现

  2. 线程加锁顺序一致

  3. 破坏环路等待条件

    • 使用非阻塞锁,如果锁被占用,释放已持有锁再尝试


二、进程间通信(IPC)

原因:进程间空间独立,无法直接通信,需要 IPC 机制实现通信。

1. 同一主机进程间通信

  • 古老方式

    • 无名管道(父子进程)

    • 有名管道(任意进程)

    • 信号(通知机制)

  • IPC 对象通信(System V)

    • 共享内存(效率最高)

    • 消息队列

    • 信号量集

2. 不同主机进程间通信

  • Socket 通信(网络通信)


3. 管道通信

无名管道

#include <unistd.h>

int pipe(int pipefd[2]);

  • 0 为读端,1 为写端

  • 默认大小 64KB

  • 特性:

    • 写阻塞:管道满时阻塞

    • 读阻塞:管道空时阻塞

    • 读返回值为 0:写端关闭且无数据

    • 管道破裂:读端关闭,写端写入出错

有名管道

#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);

  • 创建管道文件,读写端可任意进程使用

  • 其他操作:open()read()write()close()remove()


三、代码示例

1. 多线程模拟大棚数据采集

tasks.h

#ifndef __TASK_H__
#define __TASK_H__#include<pthread.h>typedef void *(*PFUN_t)(void *);typedef struct tasks
{pthread_t tid; PFUN_t ptask;  
} Task_t;extern int create_thread_tasks(Task_t *tasks, int len);
extern void destroy_thread_task(Task_t *tasks, int len);#endif

task.c

#include <stdio.h>
#include "tasks.h"int create_thread_tasks(Task_t *tasks, int len) {for (int i = 0; i < len; i++) {int ret = pthread_create(&(tasks[i].tid), NULL, tasks[i].ptask, NULL);if (ret != 0) {printf("pthread_create error!");return -1;}}return 0;
}void destroy_thread_task(Task_t *tasks, int len) {for (int i = 0; i < len; i++)pthread_join(tasks[i].tid, NULL);
}

main.c(线程同步示例)

#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>sem_t sem_w, sem_e, sem_r;void *task1(void *arg) {while (1) {sem_wait(&sem_w);printf("A\n");sem_post(&sem_e);sleep(1);}
}void *task2(void *arg) {while (1) {sem_wait(&sem_e);printf("B\n");sem_post(&sem_r);sleep(1);}
}void *task3(void *arg) {while (1) {sem_wait(&sem_r);printf("C\n");sem_post(&sem_w);sleep(1);}
}int main() {pthread_t tid[3];sem_init(&sem_w, 0, 1);sem_init(&sem_e, 0, 0);sem_init(&sem_r, 0, 0);pthread_create(&tid[0], NULL, task1, NULL);pthread_create(&tid[1], NULL, task2, NULL);pthread_create(&tid[2], NULL, task3, NULL);for (int i = 0; i < 3; i++)pthread_join(tid[i], NULL);sem_destroy(&sem_w);sem_destroy(&sem_e);sem_destroy(&sem_r);return 0;
}

2. 无名管道通信示例

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/wait.h>int main() {int pipefd[2];pipe(pipefd);pid_t pid = fork();if(pid > 0) { // 父进程close(pipefd[0]);char buff[1024] = {0};fgets(buff, sizeof(buff), stdin);write(pipefd[1], buff, strlen(buff));close(pipefd[1]);wait(NULL);} else if(pid == 0) { // 子进程close(pipefd[1]);char buff[1024] = {0};read(pipefd[0], buff, sizeof(buff));printf("%s\n", buff);close(pipefd[0]);}return 0;
}

3. 有名管道通信示例

写端

int fd = open("./myfifo", O_WRONLY);
char buff[1024] = {0};
fgets(buff, sizeof(buff), stdin);
write(fd, buff, strlen(buff));
close(fd);

读端

int fd = open("./myfifo", O_RDONLY);
char buff[1024] = {0};
read(fd, buff, sizeof(buff));
printf("%s\n", buff);
close(fd);

四、Tips

  1. 代码设计原则

    • 高内聚:增强复用性

    • 低耦合:模块间关联度低

    • 开闭原则:对扩展开放,对修改关闭

  2. 互斥锁初始化:可以使用系统宏 PTHREAD_MUTEX_INITIALIZER

  3. 内存初始化

void *memset(void *s, int c, size_t n);

  • 功能:将内存区域填充指定数据

  1. 通信方式分类

    • 单工:广播

    • 半双工:对讲机

    • 全双工:打电话

http://www.lryc.cn/news/624733.html

相关文章:

  • Tomcat Wrapper源码解析:深入理解Servlet生命周期与请求分发机制
  • 【81页PPT】国内某知名大型制药企业制药数字化转型项目汇报方案(附下载方式)
  • Leetcode 3650. Minimum Cost Path with Edge Reversals
  • Linux学习:实现简单的共享内存通信
  • 06多段代码复杂度合成规则
  • 学习日志37 python
  • [优选算法专题二滑动窗口——水果成篮]
  • PyTorch数据处理工具箱(数据处理工具箱概述)
  • 【JavaEE】(16) Spring Boot 日志
  • C语言关于函数传参和返回值的一些想法
  • 《亚矩阵云手机重构出租接单:KVM 虚拟化与边缘计算驱动的设备替代技术路径》
  • Highcharts for Flutter 正式发布
  • SQL语法大全指南
  • 【Day 29 】Linux-数据库
  • 设计模式(四)——责任链模式
  • 福彩双色球第2025095期篮球号码分析
  • 19.8 《3步实现OPT-6.7B无损量化:用自定义数据集省70%显存,精度仅跌2.3%》
  • 终极方案!lightRag/graphRag离线使用tiktoken持续报错SSLError,不改源码,彻底解决!
  • 海洋牧场邂逅海洋旅游:碰撞出新业态的璀璨火花
  • 北斗安心联车辆管理系统优势分析
  • 飞机起落架轮轴深孔中间段电解扩孔内轮廓检测 - 激光频率梳 3D 轮廓检测
  • Conda技巧:修改Conda环境目录,节省系统盘空间
  • 【每天学点‘音视频’】前向纠错 和 漏包重传
  • vue从入门到精通:搭建第一个vue项目
  • 表格内容对比及标记
  • PLC无线组网实现多台RGV搬运机器人输送系统通讯案例
  • SSM从入门到实战:1.4 Spring Bean的生命周期管理
  • 【STM32】STM32H750 CubeMX 配置 USB CDC 虚拟串口笔记
  • ThinkPHP的安装运行和调试
  • MCP协议演进:从SSE到Streamable HTTP的技术革命