Linux——多线程的控制

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、小概念

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

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


总结

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

计算机网络 (49)网络安全问题概述

前言 计算机网络安全问题是一个复杂且多维的领域&#xff0c;它涉及到网络系统的硬件、软件以及数据的安全保护&#xff0c;确保这些元素不因偶然的或恶意的原因而遭到破坏、更改或泄露。 一、计算机网络安全的定义 计算机网络安全是指利用网络管理控制和技术措施&#xff0c;保…

TCP状态转移图详解

状态 描述 LISTEN represents waiting for a connection request from any remote TCP and port. SYN-SENT represents waiting for a matching connection request after having sent a connection request. SYN-RECEIVED represents waiting for a confirming connect…

VUE学习笔记(入门)5__vue指令v-html

v-html是用来解析字符串标签 示例 <!doctype html> <html lang"en"> <head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>Document<…

【论文投稿】探秘计算机视觉算法:开启智能视觉新时代

目录 引言 一、计算机视觉算法基石&#xff1a;图像基础与预处理 二、特征提取&#xff1a;视觉信息的精华萃取 三、目标检测&#xff1a;从图像中精准定位目标 四、图像分类&#xff1a;识别图像所属类别 五、语义分割&#xff1a;理解图像的像素级语义 六、计算机视觉…

【Java数据结构】Java对象的比较

元素的比较 基本类型比较 在Java中基本类型比较可以直接比较大小 &#xff0c;返回一个布尔类型&#xff08;true或者false&#xff09;。 int a 10; int b 20; System.out.println(a>b); System.out.println(ab); System.out.println(a<b);对象比较的问题 对象的比…

《自动驾驶与机器人中的SLAM技术》ch8:基于预积分和图优化的紧耦合 LIO 系统

和组合导航一样&#xff0c;也可以通过预积分 IMU 因子加上雷达残差来实现基于预积分和图优化的紧耦合 LIO 系统。一些现代的 Lidar SLAM 系统也采用了这种方式。相比滤波器方法来说&#xff0c;预积分因子可以更方便地整合到现有的优化框架中&#xff0c;从开发到实现都更为便…

Ubuntu 24.04 LTS 更改软件源

Ubuntu 24.04 LTS 修改软件源

【2024年度技术总结】Unity 游戏开发的深度探索与实践

文章目录 前言一、Unity 游戏开发的技术深度总结1、C# 编程基础2、Unity 基础入门3、Unity 实战技巧4、Unity 小技巧分享 二、技术工具与平台的年度使用心得1、学习资源的选择2、开发环境配置3、测试与调试工具 三、技术项目实战经验与成果展示1、【制作100个Unity游戏】专栏2、…

ingress-nginx代理tcp使其能外部访问mysql

一、helm部署mysql主从复制 helm repo add bitnami https://charts.bitnami.com/bitnami helm repo updatehelm pull bitnami/mysql 解压后编辑values.yaml文件&#xff0c;修改如下&#xff08;storageclass已设置默认类&#xff09; 117 ## param architecture MySQL archit…

Top期刊算法!RIME-CNN-BiLSTM-Attention系列四模型多变量时序预测

Top期刊算法&#xff01;RIME-CNN-BiLSTM-Attention系列四模型多变量时序预测 目录 Top期刊算法&#xff01;RIME-CNN-BiLSTM-Attention系列四模型多变量时序预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 基于RIME-CNN-BiLSTM-Attention、CNN-BiLSTM-Attention、R…

游戏引擎学习第84天

仓库:https://gitee.com/mrxiao_com/2d_game_2 我们正在试图弄清楚如何完成我们的世界构建 上周做了一些偏离计划的工作&#xff0c;开发了一个小型的背景位图合成工具&#xff0c;这个工具做得还不错&#xff0c;虽然是临时拼凑的&#xff0c;但验证了背景构建的思路。这个过…

搭建一个基于Spring Boot的数码分享网站

搭建一个基于Spring Boot的数码分享网站可以涵盖多个功能模块&#xff0c;例如用户管理、数码产品分享、评论、点赞、收藏、搜索等。以下是一个简化的步骤指南&#xff0c;帮助你快速搭建一个基础的数码分享平台。 — 1. 项目初始化 使用 Spring Initializr 生成一个Spring …

31、【OS】【Nuttx】OSTest分析(1):stdio测试(一)

背景 接上篇wiki 30、【OS】【Nuttx】构建脚本优化&#xff0c;引入待构建项目参数 最小系统分析完后&#xff0c;下一个能够更全面了解Nuttx的Demo&#xff0c;当然选择OSTest&#xff0c;里面有大量关于OS的测试用例&#xff0c;方便对Nuttx的整体功能有个把握。 stdio_tes…

Spring WebFlux

文章目录 一、概述1、Spring体系定位2、Spring MVC和WebFlux差异 二、入门1、依赖2、ReactorHttpHandlerAdapter&#xff08;main启动&#xff09;3、DispatcherHandler&#xff08;SpringWebFlux启动&#xff09;4、WebFilter 三、DispatcherHandler理解1、handle 前置知识&am…

基于SSM的自助购药小程序设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…

Oracle graph 图数据库体验-安装篇

服务端安装 环境准备 安装数据库 DOCKER 安装23AI FREE &#xff0c;参考&#xff1a; https://container-registry.oracle.com/ords/f?p113:4:111381387896144:::4:P4_REPOSITORY,AI_REPOSITORY,AI_REPOSITORY_NAME,P4_REPOSITORY_NAME,P4_EULA_ID,P4_BUSINESS_AREA_ID:1…

CSS 的基础知识及应用

前言 CSS&#xff08;层叠样式表&#xff09;是网页设计和开发中不可或缺的一部分。它用于描述网页的视觉表现&#xff0c;使页面不仅实现功能&#xff0c;还能提供吸引人的用户体验。本文将介绍 CSS 的基本概念、语法、选择器及其在提升网页美观性方面的重要性。 什么是 CSS&…

C语言之装甲车库车辆动态监控辅助记录系统

&#x1f31f; 嗨&#xff0c;我是LucianaiB&#xff01; &#x1f30d; 总有人间一两风&#xff0c;填我十万八千梦。 &#x1f680; 路漫漫其修远兮&#xff0c;吾将上下而求索。 C语言之装甲车库车辆动态监控辅助记录系统 目录 一、前言 1.1 &#xff08;一&#xff09;…

python+django+Nacos实现配置动态更新-集中管理配置(实现mysql配置动态读取及动态更新)

一、docker-compose.yml 部署nacos服务 version: "3" services:mysql:container_name: mysql# 5.7image: mysql:5.7environment:# mysql root用户密码MYSQL_ROOT_PASSWORD: rootTZ: Asia/Shanghai# 初始化数据库(后续的初始化sql会在这个库执行)MYSQL_DATABASE: nac…

OpenEuler学习笔记(一):常见命令

OpenEuler是一个开源操作系统&#xff0c;有许多命令可以用于系统管理、软件安装、文件操作等诸多方面。以下是一些常见的命令&#xff1a; 一、系统信息查看命令 uname 用途&#xff1a;用于打印当前系统相关信息&#xff0c;如内核名称、主机名、内核版本等。示例&#xff…