Linux下线程间的通信

news2024/11/18 5:31:30

为什么需要线程通信?

  • 线程是操作系统调度的最小单元,拥有自己的栈空间。
  • 如果线程之间孤立运行,可能会导致资源浪费。
  • 线程需要协调工作以完成共同的任务,这就需要线程间相互通信

在 Linux 系统中,线程间通信(Inter-Thread Communication, ITC)是多线程程序设计中的一个重要方面。由于线程共享相同的内存空间,它们可以直接通过读写共享变量来进行通信。

线程间通信的类型:

主线程向子线程传递参数

  • 通过 pthread_create 函数的第四个参数 arg 传递。
  • arg 参数是一个 void* 类型的指针,可以指向任何类型的数据。
 int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                          void *(*start_routine) (void *), void *arg);

 示例代码:

void* thread(void* args) 
{
    int num = *(int*)args;
    printf("num = %d\n", num);
    pthread_exit(NULL);
}

int main() {
    pthread_t thread_id;
    int num = 100;
    if (pthread_create(&thread_id, NULL, thread, &num) != 0) {
        fprintf(stderr, "pthread_creat error\n");
        exit(EXIT_FAILURE);
    }
    printf("thread id is %ld\n", thread_id);
    pthread_join(thread_id, NULL);
    return 0;
}

 子线程向主线程传递参数

  • 子线程通过 pthread_exit 函数的 retval 参数返回值。
void pthread_exit(void *retval);
  • 主线程通过 pthread_join 函数的第二个参数 retval 获取返回值。
int pthread_join(pthread_t thread, void **retval);

示例代码:

void* do_thread_function(void* args) {
    static float score = 92.3;
    pthread_exit(&score);
}

int main() {
    pthread_t thread_id;
    if (pthread_create(&thread_id, NULL, do_thread_function, NULL) != 0) {
        fprintf(stderr, "pthread_creat error\n");
        exit(EXIT_FAILURE);
    }
    printf("thread id is %ld\n", thread_id);
    void* res = NULL;
    pthread_join(thread_id, &res);
    printf("*res = %.2f\n", *(float*)res);
    return 0;
}

 线程通信的同步机制:

 在Linux环境下,线程间的通信主要依赖于操作系统提供的同步机制,以确保线程安全地访问共享资源。

互斥锁(Mutex)

线程互斥锁(Mutex)是线程间通信中最基本的同步机制之一,用于保护临界区,确保同一时刻只有一个线程可以访问共享资源。

a.为什么要使用互斥锁?

使用线程互斥锁的主要原因是为了解决多线程程序中的数据竞争问题,确保数据的一致性和线程的同步执行。

  • 当多个线程同时访问和修改同一数据时,如果没有适当的同步机制,可能会导致数据不一致。线程互斥锁可以确保在任何时刻只有一个线程能够访问和修改共享数据。
  • 在多线程环境中,如果多个线程同时对数据进行读写操作,可能会导致数据的不一致性。互斥锁可以确保在修改数据时,其他线程不能访问该数据,从而保证数据的完整性
  • 互斥锁可以用来控制线程的执行顺序,确保线程按照预期的顺序执行。例如,在一个线程完成某些初始化工作之前,其他线程需要等待

b.互斥锁的基础概念

线程互斥锁的工作原理:
  1. 锁定(Lock):当一个线程想要访问共享资源时,它必须先获取互斥锁。如果互斥锁已经被其他线程持有,则该线程将被阻塞,直到互斥锁被释放。
  2. 解锁(Unlock):当线程完成对共享资源的访问后,它会释放互斥锁,这样其他等待的线程就可以获取互斥锁并访问共享资源
线程互斥锁的特点:
  • 互斥性:确保同一时刻只有一个线程可以持有锁。
  • 原子性:获取和释放锁的操作是原子的,即它们是不可分割的。
  • 死锁避免:通过正确的使用互斥锁,可以避免死锁的发生

c.线程互斥锁初始化

线程互斥锁的初始化是使用互斥锁之前的重要步骤,它为互斥锁设置了一个初始状态。在POSIX线程(pthread)库中,互斥锁的初始化可以通过两种方式进行:静态初始化和动态初始化

1.静态初始化

静态初始化是在编译时进行的,适用于在程序开始执行之前就已经确定了互斥锁的使用场景

#include <pthread.h>

pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
2.动态初始化

动态初始化是在程序执行过程中进行的,提供了更多的灵活性,比如可以设置互斥锁的属性

函数头文件:
#include <pthread.h>

函数原型:
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
           const pthread_mutexattr_t *restrict attr);

函数参数:
mutex:指向 pthread_mutex_t 类型变量的指针,这个变量用于存储互斥锁的状态。
attr(可选):指向 pthread_mutexattr_t 类型变量的指针,这个变量包含了互斥锁的属性。如果不需要设置特殊属性,可以传递 NULL 来使用默认属性

函数返回值:
成功:返回0,表示函数调用成功,互斥锁初始化完成。
失败:返回错误码,表示函数调用失败

错误码:
EAGAIN:系统缺乏必要的资源(除了内存)来初始化另一个互斥锁
ENOMEM:内存不足,无法初始化互斥锁
EINVAL:提供的属性无效
EPERM:调用者没有执行操作的权限
3.销毁互斥锁

当互斥锁不再使用时,应该使用pthread_mutex_destroy函数销毁它,以释放任何资源

函数头文件:
#include <pthread.h>

函数原型:
int pthread_mutex_destroy(pthread_mutex_t *mutex);

函数参数:
mutex:指向 pthread_mutex_t 类型变量的指针,这个变量表示要销毁的互斥锁。

函数返回值:
成功:返回0,表示函数调用成功
失败:返回错误码,表示函数调用失败

错误码:
EBUSY:互斥锁仍然被某个线程持有,无法销毁。
EINVAL:mutex 参数无效或未初始化

d.线程互斥锁的操作

1.获取锁
函数头文件:
#include <pthread.h>

函数原型:
int pthread_mutex_lock(pthread_mutex_t *mutex);

函数参数:
mutex:指向 pthread_mutex_t 类型变量的指针,表示要锁定的互斥锁

函数返回值:
成功:返回0,表示函数调用成功
失败:返回错误码,指示错误

错误码:
EBUSY:互斥锁已被其他线程锁定,且无法立即获得。
EAGAIN:表示系统资源不足,无法立即锁定互斥锁。这通常发生在系统资源限制,如达到最大互斥锁数量时。
EINVAL:mutex 参数无效,表示互斥锁对象无效或未正确初始化。
EPERM:当设置了一个健壮(robust)互斥锁,并且互斥锁的原拥有者终止了,而没有解锁。新线程试图锁定这个互斥锁时,会得到此错误
2.释放锁
函数头文件:
#include <pthread.h>

函数原型:
int pthread_mutex_unlock(pthread_mutex_t *mutex);

函数参数:
mutex:指向 pthread_mutex_t 类型变量的指针,表示要锁定的互斥锁

函数返回值:
成功:返回0,表示函数调用成功
失败:返回错误码,指示错误

错误码:
EPERM:调用线程不拥有互斥锁。在POSIX标准中,试图解锁一个未锁定的互斥锁或由其他线程持有的互斥锁会导致这个错误。
EINVAL:mutex 参数无效,可能是因为它未初始化,或者是一个损坏的互斥锁对象

 互斥锁示例代码(使用动态初始化):

#include <pthread.h>
#include <stdio.h>

pthread_mutex_t mutex;

void* thread_function(void* arg) {
    // 获取锁
    int result = pthread_mutex_lock(&mutex);
    if (result != 0) {
        fprintf(stderr, "互斥锁锁定失败: %s\n", strerror(result));
        exit(EXIT_FAILURE);
    }

    // 访问共享资源
    printf("访问共享资源\n");

    // 释放锁
    result = pthread_mutex_unlock(&mutex);
    if (result != 0) {
        fprintf(stderr, "互斥锁解锁失败: %s\n", strerror(result));
        exit(EXIT_FAILURE);
    }

    return NULL;
}

int main() {
    int result = pthread_mutex_init(&mutex, NULL);
    if (result != 0) {
        fprintf(stderr, "互斥锁初始化失败: %s\n", strerror(result));
        exit(EXIT_FAILURE);
    }

    pthread_t thread;
    result = pthread_create(&thread, NULL, thread_function, NULL);
    if (result != 0) {
        fprintf(stderr, "线程创建失败: %s\n", strerror(result));
        exit(EXIT_FAILURE);
    }

    pthread_join(thread, NULL);
    pthread_mutex_destroy(&mutex);

    return 0;
}

结语:

无论你是初学者还是有经验的开发者,我希望我的博客能对你的学习之路有所帮助。如果你觉得这篇文章有用,不妨点击收藏,或者留下你的评论分享你的见解和经验,也欢迎你对我博客的内容提出建议和问题。每一次的点赞、评论、分享和关注都是对我的最大支持,也是对我持续分享和创作的动力

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2169568.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

请问这张图是ai绘画吗?

小白可做&#xff01;全自动AI影视解说一键成片剪辑工具https://docs.qq.com/doc/DYnl6d0FLdHp0V2ll 先来结论&#xff0c;找到用这图的某宝店老板了&#xff0c;老板说不是AI画的。 真相只有一个&#xff0c;有图有真相 再来化身柯南&#xff0c;看看寻找答案的过程 要判断这张…

重考!CSP-J/S 2024第一轮认证泄题后续进展及疑问

2024年9月26日晚间&#xff0c;NOI官网发布了”对部分被认证者进行CSP-J/S 2024第一轮重新认证的公告“&#xff0c;对少儿编程培训机构老师举报陕西鸿泉教育培训机构泄题一事进行了最新的回应。 对涉嫌参加该机构培训的学生的CSP-J/S 2024第一轮入门级、提高级认证成绩不予认可…

<<编码>> 第 21 章 总线连接(Get on the Bus) 示例电路

比较器 info::操作说明 鼠标单击开关切换开合状态 鼠标单击逻辑输入切换 0|1 状态 若上方和左边的输入相同, 则输出高电平 primary::在线交互操作链接 https://cc.xiaogd.net/?startCircuitLinkhttps://book.xiaogd.net/code-hlchs-examples/assets/circuit/code-hlchs-ch21-0…

力扣(LeetCode)每日一题 2207. 字符串中最多数目的子序列

题目链接https://leetcode.cn/problems/maximize-number-of-subsequences-in-a-string/description/?envTypedaily-question&envId2024-09-24 给你一个下标从 0 开始的字符串 text 和另一个下标从 0 开始且长度为 2 的字符串 pattern &#xff0c;两者都只包含小写英文字母…

Vue中使用Hls.js进行视频直播的播放

HLS.js使用文档 1、安装组件&#xff1a; npm install hls.js --save2、引入组件&#xff1a; import Hls from hls.js3、使用组件&#xff1a; // DOM&#xff1a; <video id"video" controls loop"false"></video> // DATA: let hls nul…

[深度学习]卷积神经网络CNN

1 图像基础知识 import numpy as np import matplotlib.pyplot as plt # 图像数据 #imgnp.zeros((200,200,3)) imgnp.full((200,200,3),255) # 可视化 plt.imshow(img) plt.show() # 图像读取 imgplt.imread(img.jpg) plt.imshow(img) plt.show() 2 CNN概述 卷积层convrelu池…

实战OpenCV之色彩空间转换

基础入门 色彩空间是描述颜色的一种数学模型&#xff0c;它定义了颜色的三个或更多维度&#xff0c;比如&#xff1a;亮度、色相和饱和度等。最著名的色彩空间之一是RGB&#xff0c;它基于人眼对光的感知原理&#xff0c;通过红、绿、蓝三种基本颜色的不同强度组合来表示几乎所…

【HarmonyOS】鸿蒙仿iOS线性渐变实现

【HarmonyOS】仿照IOS中可以通过输入start&#xff08;0&#xff0c;0&#xff09;&#xff0c;end&#xff08;1&#xff0c;1&#xff09;获取角度到.linearGradient&#xff0c;从而实现左上到右下渐变 class Point {x: number 0y: number 0 }Entry Component struct Page…

开源链动 2+1 模式 S2B2C 商城小程序:激活 KOC,开启商业新征程

摘要&#xff1a;本文深入探讨了 KOC 在立体连接中的重要性&#xff0c;以及如何通过开源链动 21 模式 S2B2C 商城小程序发现和找到更多的 KOC。强调了历史积累强关系和快速强化强关系的方法&#xff0c;并阐述了该商城小程序在推动商业发展中的关键作用。 一、引言 在当今竞争…

mysql 内存被打满记录

一&#xff1a;早上收到报警&#xff1a;提示&#xff1a;您的云数据库RDS的1个实例因存储空间满将被锁定&#xff0c;请关注实例的存储空间使用情况&#xff0c;可通过存储扩容或空间清理解除锁定。后续查看错误日志如下&#xff1a;磁盘没有空间了 没有多余的空间写binlog和…

2024年下安徽省事业编考试报名流程(电脑)

2024年下安徽省事业编考试报名流程&#xff08;电脑&#xff09;

极狐GitLab 17.4 升级指南

GitLab 是一个全球知名的一体化 DevOps 平台&#xff0c;很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab https://dl.gitlab.cn/6y2wxugm 是 GitLab 在中国的发行版&#xff0c;专门为中国程序员服务。可以一键式部署极狐GitLab。 本文分享极狐GitLab 17.4 升级…

【JVM】垃圾释放方式:标记-清除、复制算法、标记-整理、分代回收

文章目录 1. 标记-清除2. 复制算法4. 标记-整理4. 分代回收 把标记为垃圾的对象的内存空间进行释放。主要有三种释放方式 1. 标记-清除 把标记为垃圾的对象&#xff0c;直接释放掉&#xff08;最朴素的做法&#xff09; 此时就是把标记为垃圾的对象所对应的内存空间直接释放。…

【机器学习】探索LSTM:深度学习领域的强大时间序列处理能力

目录 &#x1f354; LSTM介绍 &#x1f354; LSTM的内部结构图 2.1 LSTM结构分析 2.2 Bi-LSTM介绍 2.3 使用Pytorch构建LSTM模型 2.4 LSTM优缺点 &#x1f354; 小结 学习目标 &#x1f340; 了解LSTM内部结构及计算公式. &#x1f340; 掌握Pytorch中LSTM工具的使用. &…

反光柱定位算法-雷达强度数据包

反光柱定位算法-雷达强度数据包 反光柱定位算法-雷达强度数据包 作者&#xff1a; 苏凯 系统环境&#xff1a; 系统&#xff1a;ubuntu20.04 ros1版本&#xff1a; noetic 雷达: sick TM581 强度值标定文件&#xff1a; scanIntensities.txt 部署在环境中的反光柱数据…

类和对象(2)

文章目录 &#x1f3af;引言&#x1f453;类和对象(2)1.类的默认成员函数2.构造函数2.1构造函数概念 3.析构函数3.1. **析构函数的定义**3.2. **析构函数的特点** 4.拷贝构造函数4.1. **拷贝构造函数的定义** 5.赋值运算符重载5.1运算符重载5.2赋值运算符重载5.3日期类的实现 &…

smtp-server: 535 Error: authentication faile

问题描述&#xff1a; 在linux服务器上使用 mailx发送邮件时提示&#xff1a;smtp-server: 535 Error: authentication faile 原因&#xff1a;没有配置授权码或者授权码不正确 解决办法&#xff1a;配置授权码(以网易邮箱为例) 1. 进入网易邮箱网页版&#xff0c;打开 POP…

数据中心里全速运行的处理器正在浪费能源

数据中心是耗电大户&#xff0c;运营商一直在努力解决的一个关键问题是如何减少能源和资源消耗。人们已经找到了一些巧妙的解决方案&#xff0c;例如使用非饮用水来冷却设备&#xff0c;但一个显而易见的解决方案似乎被忽略了&#xff1a;启用处理器的各种省电功能。 随着需求的…

进程概念以及进程相关函数的使用

1.进程相关概念 1.1 程序和进程 程序&#xff0c;是指编译好的二进制文件&#xff0c;在磁盘上&#xff0c;不占用系统资源(cpu、内存、打开的文件、设备、锁....) 进程&#xff0c;是一个抽象的概念&#xff0c;与操作系统原理联系紧密。进程是活跃的程序&#xff0c;占用系…

Qt-QGroupBox容器类控件(39)

目录 容器类控件 描述 属性 使用 容器类控件 描述 这个是用来分组的&#xff0c;即把控件分组 使⽤ QGroupBox 实现⼀个带有标题的分组框.可以把其他的控件放到⾥⾯作为⼀组.这样看起来能更好看⼀点 属性 title分组框的标题alignment分组框内部内容的对⻬⽅式flat是否是…