【Linux】线程函数和线程同步详细整理(金针菇般细)

news2024/11/14 14:07:51

目录

一,线程函数

1.获取当前线程ID

2.创建线程

3.退出线程

4.阻塞线程

5.分离线程

6.取消线程

7.线程比较

8.测试代码(线程函数总结)

二,线程同步

1.互斥锁

2.读写锁

3.条件变量

4.信号量


一,线程函数

  • 参考文章: 线程 | 爱编程的大丙

man command                //Linux终端命令

# 查阅 command 命令的使用手册,包含了绝大部分的命令和函数的详细使用说明

获取当前线程IDpthread_t pthread_self(void);
创建线程int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *),void *arg);
退出线程void pthread_exit(void *retval);
阻塞线程int pthread_join(pthread_t thread, void **retval);
分离线程int pthread_detach(pthread_t thread);
取消线程(杀死线程)int pthread_cancel(pthread_t thread);
线程比较int pthread_equal(pthread_t t1, pthread_t t2);

1.获取当前线程ID

//获取当前线程的线程ID,ID类型为pthread_t,它是一个无符号长整型数
pthread_t pthread_self(void);
  • 返回值: 永远返回成功,返回被调用者的线程ID

2.创建线程

//在进程中调用线程创建函数来创建一个子线程
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *),void *arg);
参数介绍
thread输出参数,线程创建成功会将线程ID写入到该指针所指向的内存中
attr线程的属性,一般设置为NULL,即线程的创建使用默认属性
start_routine函数指针,子线程的业务处理函数,即传入的函数指针会在该子线程中执行
arg函数参数,和start_routine指针指向的函数参数匹对,即该参数是传递给start_routine指针所指向的函数
  • 返回值:创建成功返回0,创建失败返回一个错误号并且*thread值未定义

3.退出线程

//线程退出不会影响到其它线程的正常运行,子线程或主线程都可以调用
void pthread_exit(void *retval);
  • 参数:被调用线程退出时,其它线程可通过retval获取线程退出时携带的数据。不需要数据可以指定为NULL

4.阻塞线程

//函数被调用一次,只会回收一个子线程,如果有多个线程需要循环传入线程ID进行回收
int pthread_join(pthread_t thread, void **retval);
  • 参数介绍
    • thread:要被阻塞的线程ID,指定的线程必须是可被阻塞的
    • retval:所指向一级指针的地址是一个输出参数,该地址中存储了pthread_exit()传出的数据,不需要数据可以指定为NULL
  • 返回值:成功返回0,失败返回一个错误码

5.分离线程

/*
1.传入分离子线程的线程ID,便会与主线程分离,当子线程退出的时候,其占用的资源就会被系统的其它进程接管并回收。
2.不可对同一线程重复分离,不然会出现未定义行为
*/
int pthread_detach(pthread_t thread);

6.取消线程

int pthread_cancel(pthread_t thread);

7.线程比较

int pthread_equal(pthread_t t1, pthread_t t2);

8.测试代码(线程函数总结)

#include <cstdio>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

//子线程处理代码
void* threadFun(void *_arg){
    printf("子线程线程ID:%ld 传入参数值:%ld\n", pthread_self(),*(int*)_arg);
    for (int i = 0; i < 5; i++)
    {
        sleep(1);
        printf("subThread-i: = %d\n", i);
        if (i == 3) {
            int* val = (int*)malloc(sizeof(int));
            *val = i;
            pthread_exit(val);  //子线程退出携带的数据val可以被主线程中调用pthread_join获取
        }
    }
    return nullptr;
}

int main()
{
    printf("主线程线程ID:%ld\n", pthread_self());
    for (int i = 0; i < 5; i++)
    {
        sleep(1);
        printf("main-i: = %d\n", i);
    }

    pthread_t tid;  //创建一个子线程
    int thread_arg = 1008;   //传给threadFun()线程工作函数的参数
    int flag = pthread_create(&tid, nullptr, threadFun, (void*)&thread_arg);
    if (flag == 0)
    {
        printf("子线程创建成功,线程ID:%ld  传入threadFun()线程函数的参数值:%d\n", tid,thread_arg);
    }
   
    //pthread_detach(tid);    
    /*
    * 线程分离之后在主线程使用pthread_join()就会报段错误(核心已转储)
    * ,因为子线程退出时,其占用的内核资源被系统其它进程回收了,然而你又对它进行操作。
    */
    void *subTd_retval = nullptr;

    pthread_join(tid, &subTd_retval);   //阻塞子线程,并获取子线程退出时的数据
    printf("子线程 %ld 返回的数据:%ld\n", tid, *(int*)subTd_retval);
    
    pthread_detach(tid);    //这里让子线程与主线程分离
    pthread_exit(nullptr);  //让主线程自己退出
    return 0;
}

二,线程同步

 参考文章: 线程同步 | 爱编程的大丙

1.当多个线程对共享资源(多个线程共同访问的变量)进行访问的时候就会出现数据混乱的问题,所以就需要进行线程同步,所谓的线程同步实际上是各线程按先后顺序依次对共享资源进行访问,而不是同时进行的,其实也就是让各线程去抢占CPU时间片,抢到就访问数据,没抢到就挂起,这之间会涉及到上下文的切换。虽然降低了运行效率,但提高了数据访问的安全性,这是值得的。

2.线程同步有四种方式:互斥锁,读写时,条件变量,信号量。

3.互斥锁,读写时,条件变量在头文件pthread中,信号量在头文件semaphore中。

1.互斥锁

//每一个共享资源对应一把锁,锁的个数和线程的个数无关
pthread_mutex_t mutex;

//初始化互斥锁,被restrict修饰的指针可以访问指向的内存地址
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);

//释放互斥锁资源
int pthread_mutex_destroy(pthread_mutex_t *mutex);

//修改互斥锁状态,对传入的互斥锁进行加锁
int pthread_mutex_lock(pthread_mutex_t *mutex);

//对传入的互斥锁进行尝试加锁,
int pthread_mutex_trylock(pthread_mutex_t *mutex);

//对传入的互斥锁进行解锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);

2.读写锁

//读写锁是互斥锁的升级版,读锁是共享的,写锁是互斥(独占)的。写锁优先级比读锁高
pthread_rwlock_t rwlock;

//初始化读写锁,attr默认为NULL就行
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);

//释放读写锁占用的系统资源
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

//修改读写锁状态,锁定读操作,读锁可以重复锁
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);

//对传入的读写锁进行尝试加读锁
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);

//对传入的读写锁进行加写锁
int pthread_rwlock_wdlock(pthread_rwlock_t *rwlock);

//对传入的读写锁进行尝试加写锁
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);

3.条件变量

  • 条件变量不是处理线程同步的,而是进行线程的阻塞。多线程实现线程同步需要配合互斥锁来使用。
//条件变量类型的定义
pthread_cond_t cond;

//初始化条件变量,一般attr默认为NULL就行
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_cond_t *restrict attr);

//释放条件变量资源
int pthread_cond_destroy(pthread_cond_t *cond);

//线程阻塞函数,哪个线程调用这个函数就会阻塞哪个线程
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);

//唤醒在条件变量上阻塞的线程(至少一个)
int pthread_cond_signal(pthread_cond_t *cond);

//唤醒在条件变量阻塞的全部线程
int pthread_cond_broadcast(pthread_cond_t *cond);

4.信号量

  •  信号量 主要是阻塞线程,不能保证线程安全,如果要保证线程安全,需要信号量和互斥锁一起使用

1.定义信号量变量

sem_t sem;

2.初始化信号量

int sem_init(sem_t *sem, int pshared, unsigned int value);
  • 参数
    • sem:信号量变量地址
    • pshared:0-线程同步,!0-进程同步
    • value:初始化信号量*sem拥有的资源数(>=0),为0就会阻塞线程

3.销毁信号量

int sem_destroy(sem_t *sem);

4.锁住一个信号量资源,资源数=0时会阻塞线程

int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);

5.释放一个信号量资源

int sem_post(sem_t *sem);

6.获取信号量的资源数

//sval是输出参数,可以通过sval获取信号量sem中拥有的资源数量
int sem_getvalue(sem_t *sem, int *sval);

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

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

相关文章

【阿旭机器学习实战】【29】产品广告投放实战案例---线性回归

【阿旭机器学习实战】系列文章主要介绍机器学习的各种算法模型及其实战案例&#xff0c;欢迎点赞&#xff0c;关注共同学习交流。 目录问题描述数据处理过程及源码通过数据可视化分析数据训练线性回归模型可视化训练好的线性回归模型结果预测问题描述 你所在的公司在电视上做产…

mybatis狂神(附自学过程中疑问解决)

首先先附上mybatis的官方文本链接mybatis – MyBatis 3 | 简介一、Mybatis介绍MyBatis 是一款优秀的持久层框架&#xff0c;它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来…

Comparator和Comparable的区别以及Collections.sort排序原理

一、概述 Comparable和Comparator都是两个接口&#xff0c;接口都可以用来实现集合中元素的比较、排序&#xff0c;Comparator位于包java.util下&#xff0c;而Comparable位于包java.lang下&#xff0c;Comparable接口将比较代码嵌入自身类中&#xff0c;而Comparator既可以嵌…

非标题党:前端Vue React 项目编程规范化配置(大厂规范)

前端项目编程规范化配置 下述例子主要是从 代码规范化 以及 git 提交规范化 两方面进行配置。内容很多&#xff0c;请做好心理准备 一、代码检测工具 ESLint 在我们通过 vue create “项目名” 时&#xff0c;我们可以通过手动配置的方式&#xff0c;来配置 ESLint 来对代码进…

QDateTime的11种显示方式

QDateTime datetime QDateTime::currentDateTime(); datetime.toString(“hh:mm:ss\nyyyy/MM/dd”); datetime.toString(“hh:mm:ss ap\nyyyy/MM/dd”); datetime.toString(“hh:mm:ss\nyyyy-MM-dd”); datetime.toString(“hh:mm:ss ap\nyyyy-MM-dd”); datetime.to…

【分享】订阅用友YonSuite集简云连接器同步销售出库数据至用友YonSuite

方案场景 在企业中因多种系统孤立导致数据割裂&#xff0c;是现企业中现阶段面临的最大问题&#xff0c;而钉钉作为常用的OA审批系统&#xff0c;用友YonSuite作为ERP系统&#xff0c;原方式钉钉内完成审批再由人工将数据同步到用友YonSuite系统&#xff0c;数据同步过程中不仅…

将HTTP接口配置成HTTPS

一、使用Java的keytool.exe程序生成本机的TLS许可找到Java的jdk目录进入bin默认安装路径C:\Program Files\Java\jdk1.8.0_91\bin 进入命令面板&#xff0c;在bin的路径栏中输入cmd敲击回车即可使用keytoolkeytool -genkeypair -alias tomcat_https -keypass 123456 -keyalg RSA…

linux线程的基本知识

这里用的是Linux的pthread线程库&#xff0c;需要加pthread线程库。 线程的创建 第一个参数是线程id的地址。第二个参数是线程属性&#xff0c;一般为NULL。第三个是要执行的函数。第四个是函数的参数&#xff0c;一般也为NULL 线程的等待&#xff0c;第一个参数是线程的id,第…

VBA提高篇_27 OptionBOX_CheckBox_Frame_Image_VBA附加控件

文章目录1.单选按钮OptionBOX:2.复选框CheckBox:3.框架Frame:4.图像Image: (loadPictrue)5. VBA附加控件:6. 适用于很多控件的重要属性:1.单选按钮OptionBOX: 默认时,同一窗体的所有单选按钮均属于同一组,只能选中一个 可通过Frame控件进行分组解决. 2.复选框CheckBox: 一次可以…

备考软考系统分析师-1

系统分析师教程网盘资源&#xff1a;链接: https://pan.baidu.com/s/1ekHuCJJ3o5RrW1xeMkxhdA 提取码: 6666 信息系统战略规划 信息系统开发方法&#xff1a; 结构化法 瀑布模型 原型法 自顶向下 用于需求阶段较多 面向对象 自底向上 面向服务的方法 系统建模 政府信息…

在Excel接入 chatgtp (图文教学)

效果图 话不多说&#xff0c;开始教学 首先点击插入&#xff0c;然后点击获取加载项 office Excel 加载加载项时出错 解决办法_long_songs的博客-CSDN博客今天在添加维基百科的时候&#xff0c;怎么都添加不了&#xff0c;网上的办法都是关闭&#xff0c;重启&#xff0c;或者…

[架构之路-114]-《软考-系统架构设计师》-软件架构设计-7-软件架构评估

前言第7节 软件架构评估7.1 什么是架构评估/为什么要软件架构评估在软硬件系统总体架构设计完成之后&#xff0c;为保证架构设计的合理性、完整性和针对性&#xff0c;从根本上保证系统质量&#xff0c;降低成本及投资风险&#xff0c;需要对总体架构进行评估。7.2 软件架构评估…

word高效技巧:几个快速填充表格的操作方法

我们办公人员在面对大量表格数据的时候&#xff0c;都希望以最简便、快捷的方式完成对数据的填充、美化等整理工作。比如&#xff0c;日常工作中几种常用的Word表格填充类型&#xff1a;1. 填充序号&#xff1b;2. 填充文本&#xff1b;3. 填充颜色。因此&#xff0c;接下来给大…

你了解互联网APP推荐的背后逻辑么(下)?

上篇重点介绍了互联网APP在搜索交互场景下的通用逻辑&#xff0c;让大众对每天离不开的搜索进行了一个普遍介绍。这一篇&#xff0c;我们来聊聊抖音、头条等APP划一划这个动作背后&#xff0c;是怎么做推荐的。推荐的背后&#xff0c;离不开每个用户的数据&#xff0c;而且这个…

谈谈Linux内核的噪声

Linux内核是广被使用的操作系统&#xff0c;从嵌入式家用设备&#xff0c;航空航天设备到超级计算机&#xff0c;到处都有Linux内核的身影&#xff0c;这归功于Linux内核丰富的配置带来的巨大灵活性。 网络虚拟化和软件定义网络的发展&#xff0c;也从另外一个方面证实了在网络…

Java线程的6中状态

Java 线程的状态 Java线程有六种状态&#xff1a; 初始&#xff08;NEW&#xff09;、运行&#xff08;RUNNABLE&#xff09;、阻塞&#xff08;BLOCKED&#xff09;、 等待&#xff08;WAITING&#xff09;、超时等待&#xff08;TIMED_WAITING&#xff09;、终止&#xff08…

layui框架学习(8:动态操作选项卡)

Layui官网示例&#xff08;参考文献3&#xff09;中的选项卡部分除了介绍选项卡的样式外&#xff0c;还介绍了新增选项卡、删除选项卡、切换选项卡等动态操作选项卡方式&#xff0c;主要调用element模块中与选项卡相关的函数实现&#xff0c;除此之外&#xff0c;element模块还…

树莓派下安装OpenEuler

openEuler作为华为开源的应用于嵌入式设备的操作系统&#xff0c;正在受到越来越多的关注。树莓派是一个很好的应用场景&#xff0c;这篇文章就介绍下如何在树莓派上安装openEuler。   ps&#xff1a;openEuler要求树莓派的版本是4B 1.下载openEuler镜像 镜像网址&#xff1…

【K哥爬虫普法】百度、360八年乱战,robots 协议之战终落幕

我国目前并未出台专门针对网络爬虫技术的法律规范&#xff0c;但在司法实践中&#xff0c;相关判决已屡见不鲜&#xff0c;K哥特设了“K哥爬虫普法”专栏&#xff0c;本栏目通过对真实案例的分析&#xff0c;旨在提高广大爬虫工程师的法律意识&#xff0c;知晓如何合法合规利用…

大数据框架之Hadoop:MapReduce(三)MapReduce框架原理——MapReduce工作流程

1、流程示意图 MapReduce详细工作流程&#xff08;一&#xff09; MapReduce详细工作流程&#xff08;二&#xff09; 2、流程详解 上面的流程是整个MapReduce最全工作流程&#xff0c;但是Shuffle过程只是从第7步开始到第16步结束&#xff0c;具体Shuffle过程详解&#xff0…