Linux——多线程的控制

news2025/1/23 16:35:37

Linux——线程的慨念及控制-CSDN博客


文章目录


目录

文章目录

前言

一、线程函数的认识

1、基本函数的回顾

1、线程的创建pthread_create

2、线程阻塞pthread_join

3、线程退出pthread_exit

2、线程的分离pthread_detach

3、互斥锁初始化函数:pthread_mutex_init

4、互斥锁加锁函数:pthread_mutex_lock

5、互斥锁解锁函数:pthread_mutex_unlock

二、介绍锁

1、生活案例介绍锁

三、细小概念

1、线程安全:

常见的线程不安全的情况

常见的线程安全的情况

2、重入:

常见不可重入的情况

常见可重入的情况

3、小概念

总结


前言

我们在上一篇博客中学习了单线程的基本控制,及创建、阻塞、终止等操作,这篇博客介绍多个线程的控制。 


一、线程函数的认识

1、基本函数的回顾

1、线程的创建pthread_create

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);

2、线程阻塞pthread_join

int pthread_join(pthread_t thread, void **retval);

3、线程退出pthread_exit

void pthread_exit(void *retval);

2、线程的分离pthread_detach

int pthread_detach(pthread_t thread);
  • 参数说明
    • thread:要分离的线程的标识符。
  • 返回值
    • 成功时返回 0。
    • 出错时返回错误码,可通过 perror 函数输出错误信息。
  • 含义
    • 当一个线程被创建时,它的状态默认是可连接的(joinable),这意味着另一个线程可以使用 pthread_join 函数来等待它的结束,并获取其返回值。而线程分离是将线程的状态设置为已分离(detached),这样就不需要其他线程来等待它结束,当线程结束时,其资源会自动被系统回收。
    • 分离的线程在结束时会自动释放资源,包括线程的堆栈和其他系统资源,而不需要主线程或其他线程调用 pthread_join 函数来回收。
    • 对于一些后台线程,例如日志记录线程、垃圾回收线程等,它们通常在程序的整个生命周期内持续运行,并且不需要主线程等待它们结束或获取它们的返回值。将这些线程设置为分离状态可以简化线程的管理,避免主线程在结束时因为没有调用 pthread_join 而产生资源泄漏。

通俗的说就是当我们主线程不需要等待子线程的返回值时,我们可以让主线程与次线程分离开,不让主线程等待子线程的退出。

代码样例

#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <vector>
#include <string>
using namespace std;

#define NUM 4

void *Threadroutine(void *args)
{
    int cnt = 0;
    while (true)
    {
        cout << (*((string *)args)).c_str() << endl;
        sleep(1);

        if ((cnt++) == 3)
        {
            break;
        }
    }
    cout<<(*((string *)args)).c_str()<<"运行完了"<<endl;
    return nullptr;
}
int main()
{
    vector<pthread_t> tids;
    for (int i = 0; i < NUM; i++)
    {
        string *name = new string("我是线程-" + to_string(i));
        pthread_t tid;
        tids.push_back(tid);
        pthread_create(&tids[i], nullptr, Threadroutine, name);

    }
        for (auto &th : tids)
        {
            pthread_detach(th);
        }
        for (auto &e : tids)
        {
            pthread_join(e, nullptr);
        }
        while (true)
        {
            cout << "我是主线程,我没有等子线程" << endl;
            sleep(1);
        }
    return 0;
}

我们在把pthread_detach注释掉

 

   这就是分离和不分离的区别,分离以后主线程不用再在join的位置等待子线程推出后再向下运行了。

3、互斥锁初始化函数:pthread_mutex_init

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

  • 参数说明
    • mutex:一个 pthread_mutex_t 类型的指针,指向要初始化的互斥锁。
    • attr:互斥锁属性对象的指针,通常设置为 NULL 表示使用默认属性。
  • 返回值
    • 成功时返回 0。
    • 出错时返回错误码,可通过 perror 函数输出错误信息。

4、互斥锁加锁函数:pthread_mutex_lock

int pthread_mutex_lock(pthread_mutex_t *mutex);

  • 参数说明
    • mutex:一个 pthread_mutex_t 类型的指针,指向要加锁的互斥锁。
  • 返回值
    • 成功时返回 0。
    • 出错时返回错误码,可通过 perror 函数输出错误信息。

5、互斥锁解锁函数:pthread_mutex_unlock

int pthread_mutex_unlock(pthread_mutex_t *mutex);
    • mutex:一个 pthread_mutex_t 类型的指针,指向要解锁的互斥锁。
  • 返回值
    • 成功时返回 0。
    • 出错时返回错误码,可通过 perror 函数输出错误信息。

二、介绍锁

线程锁是多线程编程中的一个重要概念,主要用于解决多线程并发访问共享资源时可能出现的竞态条件

1、生活案例介绍锁

我们看图发现有一个房间,而有很多人,这些人想要进入房间,而现在门上有一个锁,这把锁只有一把钥匙在门上面,只有拥有钥匙的人才能进入房间,当a先来拿到了钥匙,然后它进入了房间,b、c、d等人都无法进入房间,因为它们没有钥匙,当a出来了,刚把门锁上,但是又想起来忘东西在里面了,钥匙他还拿着钥匙,这时候其他人还是没有办法进入;

而在计算机的角度来看这个问题就变成了房间是cup,人都是线程,当a在执行一条指令时,其他人只能看着(未进入的线程只能看着),这就是锁的作用

  • 这是最常见的线程锁类型,其核心思想是确保在同一时刻只有一个线程可以访问被保护的资源或执行被保护的代码段。
  • 当一个线程获取到互斥锁时,其他试图获取该锁的线程将被阻塞,直到锁被释放。

那么有什么优势呢?

接下来我们写一个抢票的代码看看加锁和不加锁的区别

#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <vector>
#include <string>
using namespace std;

#define NUM 4

pthread_mutex_t lock;
int tickets = 1000;
void *Threadroutine(void *args)
{
    while (true)
    {
        pthread_mutex_lock(&lock);
        if (tickets > 0)
        {
            cout << (*((string *)args)).c_str() << ",我抢到了第" << tickets << " 张票" << endl;
            tickets--;
            usleep(200);
        }
        else
        {
            pthread_mutex_unlock(&lock);
            break;

        }
        pthread_mutex_unlock(&lock);
        usleep(1);
    }
    return nullptr;
}
int main()
{
    vector<pthread_t> tids;
    pthread_mutex_init(&lock, nullptr);
    for (int i = 0; i < NUM; i++)
    {
        string *name = new string("我是线程-" + to_string(i));
        pthread_t tid;
        tids.push_back(tid);
        pthread_create(&tids[i], nullptr, Threadroutine, name);
    }
    for (auto &e : tids)
    {
        pthread_join(e, nullptr);
    }


    return 0;
}

不加锁我们看到,线程0和线程1都抢到了第122张票,为什么呢? 

我们知道只有cpu有计算能力,我们的代码都是加载到cpu上的寄存器上计算的,而cpu计算时,第一步要将数据加载到cpu上;第二步--;第三步返回数据;

当我们的线程0来了,将122加载当了cpu上时,这个时候线程1和他看到的数据122来了,那么cpu就先将进程0和他的数据放在一边,因为cpu还没有--,所以线程1看到的就是122,cpu执行完线程1的--后,将1返回,这个时候寄存器上的数字为121,cpu又将进程0放回cpu上进程--,因为cpu刚刚把线程0和他的122放到了一边,现在那会的还是122,所以线程0,返回的也是121;

加锁结果如下: 

这个时候就是抢钥匙了,就不是同时执行--了,线程0来了,就先将线程0--,然后解锁把钥匙放回去,几个线程谁快,谁先进入cpu,谁来抢票--;

三、细小概念

1、线程安全:

线程安全:多个线程并发同一段代码时,不会出现不同的结果。常见对全局变量或者静态变量进行操作,并且没有锁保护的情况下,会出现该问题。

常见的线程不安全的情况

不保护共享变量的函数

函数状态随着被调用,状态发生变化的函数返回指向静态变量指针的函数

调用线程不安全函数的函数

常见的线程安全的情况

每个线程对全局变量或者静态变量只有读取的权限,而没有写入的权限,一般来说这些线程是安全的类或者接口对于线程来说都是原子操作

多个线程之间的切换不会导致该接口的执行结果存在二义性

2、重入:

重入:同一个函数被不同的执行流调用,当前一个流程还没有执行完,就有其他的执行流再次进入,我们称之为重入。一个函数在重入的情况下,运行结果不会出现任何不同或者任何问题,则该函数被称为可重入函数,否则,是不可重入函数

常见不可重入的情况

调用了malloc/free函数,因为malloc函数是用全局链表来管理堆的

调用了标准I/O库函数,标准I/O库的很多实现都以不可重入的方式使用全局数据结构可重入函数体内使用了静态的数据结构

常见可重入的情况

不使用全局变量或静态变量

不使用用malloc或者new开辟出的空间

不调用不可重入函数

不返回静态或全局数据,所有数据都有函数的调用者提供

使用本地数据,或者通过制作全局数据的本地拷贝来保护全局数据

3、小概念

临界资源:多线程执行流共享的资源就叫做临界资源
临界区:每个线程内部,访问临界自娱的代码,就叫做临界区

互斥:任何时刻,互斥保证有且只有一个执行流进入临界区,访问临界资源,通常对临界资源起保护作用原子性(后面讨论如何实现):不会被任何调度机制打断的操作,该操作只有两态,要么完成,要么未完成


总结

程锁是多线程编程中确保数据一致性和程序正确性的重要工具,不同类型的锁适用于不同的场景,合理使用它们可以在保证程序性能的同时避免多线程并发带来的问题。

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

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

相关文章

“AI教学培训信息资源综合管理系统:让教学更精准、更高效

大家好&#xff0c;作为一名资深产品经理&#xff0c;今天我就跟大家聊聊AI教学培训信息资源综合管理系统。在这个信息爆炸的时代&#xff0c;如何高效地管理教学培训信息资源&#xff0c;成为了教育行业的一大痛点。而AI技术的融入&#xff0c;无疑为解决这个问题提供了强有力…

Net Core微服务入门全纪录(三)——Consul-服务注册与发现(下)

系列文章目录 1、.Net Core微服务入门系列&#xff08;一&#xff09;——项目搭建 2、.Net Core微服务入门全纪录&#xff08;二&#xff09;——Consul-服务注册与发现&#xff08;上&#xff09; 3、.Net Core微服务入门全纪录&#xff08;三&#xff09;——Consul-服务注…

vue3+webOffice合集

1、webOffice 初始化 1&#xff09;officeType: 文档位置&#xff1a;https://solution.wps.cn/docs/web/quick-start.html#officetype 2&#xff09;appId: 前端使用appId 后端需要用到AppSecret 3&#xff09;fileId: 由后端返回&#xff0c;前端无法生成&#xff0c;与上传文…

Python - itertools- pairwise函数的详解

前言&#xff1a; 最近在leetcode刷题时用到了重叠对pairwise,这里就讲解一下迭代工具函数pairwise,既介绍给大家&#xff0c;同时也提醒一下自己&#xff0c;这个pairwise其实在刷题中十分有用&#xff0c;相信能帮助到你。 参考官方讲解&#xff1a;itertools --- 为高效循…

【优选算法】5----有效三角形个数

又是一篇算法题&#xff0c;今天早上刚做的热乎的~ 其实我是想写博客但不知道写些什么&#xff08;就水一下啦&#xff09; -------------------------------------begin----------------------------------------- 题目解析: 这道题的题目算是最近几道算法题里面题目最短的&a…

C语言中 指针类型的意义

对于初学者也包括我来说指针不就是来存放地址的吗 应该会有一个疑惑 为什么还有那么多类型char* int*等等不都能存放一个地址无论是整形还是字符 那这个指针类型能有什么意义呢 有的有的兄弟 他的真正意义就在于解引用上面*p 指针类型决定了 指针进行解引用操作时 一次能访问…

easyexcel读取写入excel easyexceldemo

1.新建springboot项目 2.添加pom依赖 <name>excel</name> <description>excelspringboot例子</description><parent> <groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId&…

数据结构(精讲)----栈 stack

什么是栈 栈是只能在一端进行插入和删除操作的线性表(又称为堆栈)&#xff0c;进行插入和删除操作的一端称为栈顶&#xff0c;另一端称为栈底。 特点&#xff1a;栈是先进后出FILO(First In Last Out) (LIFO(Last In First Out)) 顺序栈 特性 逻辑结构&#xff1a;线性结构…

Hexo + NexT + Github搭建个人博客

文章目录 一、 安装二、配置相关项NexT config更新主题主题样式本地实时预览常用命令 三、主题设置1.侧边栏2.页脚3.帖子发布字数统计 4.自定义自定义页面Hexo 的默认页面自定义 404 页自定义样式 5.杂项搜索服务 四、第三方插件NexT 自带插件评论系统阅读和访问人数统计 五、部…

I2S是什么通信协议?它如何传输音频数据?它和I2C是什么关系?

首先我们先明确一点&#xff0c;I2S和I2C没有什么关系&#xff0c;如果非要扯点共同点的话那就是它们都是由飞利浦制定的。 I2C我们用的比较多&#xff0c;我们用的大多数的传感器模块用的通信协议就是I2C&#xff0c;SPI&#xff0c;UART这些。 而I2S应用领域比较单一&#…

大模型:LangChain技术讲解

一、什么是LangChain 1、介绍 LangChain是一个用于开发由大型语言模型提供支持的Python框架。它提供了一系列工具和组件&#xff0c;帮助我们将语言模型集成到自己的应用程序中。 有了它之后&#xff0c;我们可以更轻松地实现对话系统、文本生成、文本分类、问答系统等功能。…

【2024 博客之星评选】请继续保持Passion

我尝试复盘自己2024年走的路&#xff0c;希望能给诸君一些借鉴。 文章目录 回头望感想与收获成长与教训今年计划感恩一些体己话 回头望 回望我的2024年&#xff0c;年初拿高绩效&#xff0c;但感觉逐渐被公司一点点剥离出中心&#xff1b;年中一直在学习防患于未然&#xff1b…

Node.js接收文件分片数据并进行合并处理

前言&#xff1a;上一篇文章讲了如何进行文件的分片&#xff1a;Vue3使用多线程处理文件分片任务&#xff0c;那么本篇文章主要看一下后端怎么接收前端上传来的分片并进行合并处理。 目录&#xff1a; 一、文件结构二、主要依赖1. express2. multer3. fs (文件系统模块)4. pat…

【2025小年源码免费送】

&#x1f496;学习知识需费心&#xff0c; &#x1f4d5;整理归纳更费神。 &#x1f389;源码免费人人喜&#xff0c; &#x1f525;码农福利等你领&#xff01; &#x1f496;山高路远坑又深&#xff0c; &#x1f4d5;大军纵横任驰奔&#xff0c; &#x1f389;谁敢横刀立马行…

【JavaSE】(8) String 类

一、String 类常用方法 1、构造方法 常用的这4种构造方法&#xff1a;直接法&#xff0c;或者传参字符串字面量、字符数组、字节数组。 在 JDK1.8 中&#xff0c;String 类的字符串实际存储在 char 数组中&#xff1a; String 类也重写了 toString 方法&#xff0c;所以可以直…

css普通用法

Css普通用法 这是一个链接 W3C&#xff0c;用这个语法可以访问W3C,自己可以去看更加详细的内容。 基本语法 名字{ 类型&#xff1a;参数 类型&#xff1a;参数 }a{ color:blue }引入方法 直接在html之中进行带入到html代码之中&#xff0c;文件不需要重新写一个&#xff0c…

大数据Hadoop中MapReduce的介绍包括编程模型、工作原理(MapReduce、MapTask、ReduceTask、Shuffle工作原理)

MapReduce概述 MapReduce是Hadoop的核心项目之一&#xff0c;它是一个分布式计算框架&#xff0c; 可用于大数据并行处理的计算模型、框架和平台&#xff0c;主要解决海量数据的计算&#xff0c;是大数据中较为熟知的分布式计算框架。 MapReduce作为分布式计算框架&#xff0…

【学习笔记】计算机网络(一)

第1章 概述 文章目录 第1章 概述1.1 计算机网络在信息时代中的作用1.2 互联网概述1.2.1 网络的网络1.2.2互联网基础结构发展的三个阶段1.2.3 互联网的标准化工作 1.3 互联网的组成1.3.1 互联网的边缘部分1.3.2 互联网的核心部分 1.4 计算机网络在我国的发展1.5 计算机网络的类别…

[OpenGL]实现屏幕空间环境光遮蔽(Screen-Space Ambient Occlusion, SSAO)

一、简介 本文介绍了 屏幕空间环境光遮蔽(Screen-Space Ambient Occlusion, SSAO) 的基本概念&#xff0c;实现流程和简单的代码实现。实现 SSAO 时使用到了 OpenGL 中的延迟着色 &#xff08;Deferred shading&#xff09;技术。 按照本文代码实现后&#xff0c;可以实现以下…

KubeSphere 开源社区 2024 年度回顾与致谢

随着 2024 年圆满落幕&#xff0c;我们回顾 KubeSphere 社区这一年走过的每一步&#xff0c;感慨万千。2024 年&#xff0c;KubeSphere 继续领跑云原生技术的创新与发展&#xff0c;推动开源文化的传播&#xff0c;致力于为全球开发者和企业用户提供更强大的平台和解决方案。感…