【linux学习】多线程(1)

文章目录

  • 线程的概念
    • 线程与进程
  • 线程的用法
    • 线程的创建
      • 多线程
    • 线程的等待
    • 线程锁
      • 死锁

线程的概念

在Linux中,线程(Thread)是程序执行流的最小单位,是进程中的一个实体,负责在程序中执行代码。线程本身不拥有系统资源,但它可以访问其所属进程的资源,包括内存空间、文件句柄等。线程与进程的主要区别在于线程是共享进程资源的,而进程之间则是独立的。

线程与进程

在Linux中,线程有时被称为轻量级进程(Lightweight Process, LWP)。从内核的角度来看,线程和进程在很大程度上是相似的,它们都通过task_struct结构体来描述。但是,线程与父进程(或其他线程)共享某些资源,如地址空间和文件描述符表,而进程则拥有自己的独立资源。

在这里插入图片描述
PCB(Process Control Block)即进程控制块。
虽然线程和进程都使用task_struct来描述,但Linux内核通过一些特定的字段来区分它们。例如,每个task_struct都有一个pid(进程ID)和一个tgid(线程组ID)。对于主线程(也就是我们通常所说的“进程”),其pid和tgid是相同的。但是,对于该进程创建的其他线程,它们的pid是唯一的,但tgid与主线程的tgid(也就是该进程的ID)相同。通过这种方式,内核可以区分一个task_struct描述的是进程还是线程。
进程与线程关系图:
(波浪线代表一个线程)
在这里插入图片描述

线程的用法

线程的创建

需要pthread_creart函数来创建线程。
在这里插入图片描述

#include <iostream>
#include <unistd.h>
#include <pthread.h>
void *ThreadRoutine(void *args)
{const char *threadname = (const char *)args;while (true){std::cout << "i am a new: " << threadname << std::endl;sleep(1);}
}
int main()
{pthread_t tid;pthread_create(&tid, nullptr, ThreadRoutine, (void *)"thread 1");while(true){std:: cout << "i am main thread" << std::endl;sleep(1);}return 0;
}

在这里插入图片描述
看图可以得知我们创建了一个线程,主线程仍然在继续运行。

多线程

void *ThreadRoutine(void *args)
{const char *threadname = (const char *)args;while (true){std::cout << "i am a new: " << threadname << std::endl;sleep(3);}
}
int main()
{pthread_t tid1;pthread_create(&tid1, nullptr, ThreadRoutine, (void *)"thread 1");sleep(3);pthread_t tid2;pthread_create(&tid2, nullptr, ThreadRoutine, (void *)"thread 2");sleep(3);pthread_t tid3;pthread_create(&tid3, nullptr, ThreadRoutine, (void *)"thread 3");sleep(3);pthread_t tid4;pthread_create(&tid4, nullptr, ThreadRoutine, (void *)"thread 4");sleep(3);while (true){std::cout << "i am main thread" << std::endl;sleep(3);}return 0;
}

可以看出线程2,3,4都正常运行。
在这里插入图片描述

线程的等待

在这里插入图片描述
pthread_t thread:这是你想要等待的线程的标识符。这个标识符是在调用pthread_create时返回的。
**void retval:这是一个指向指针的指针,用于获取被等待线程的返回值。如果retval不是NULL,那么pthread_join会将终止线程的返回值放在*retval所指向的位置。如果你对线程的返回值不感兴趣,可以将这个参数设置为NULL。

#include <iostream>
#include <unistd.h>
#include <pthread.h>
void *ThreadRoutine(void *args)
{int cnt = 5;const char *threadname = (const char *)args;while (cnt){std::cout << "i am a new: " << threadname << std::endl;sleep(1);cnt--;}return args;
}
int main()
{pthread_t tid1;pthread_create(&tid1, nullptr, ThreadRoutine, (void *)"thread 1");void *ret = nullptr;pthread_join(tid1,&ret);std:: cout << (char *)ret << std::endl;sleep(3);//while ()//{std::cout << "i am main thread" << std::endl;sleep(1);//}return 0;
}

在这里插入图片描述
在函数体内,我们将args作为返回值,可以看到将函数的返回值通过pthread_join()函数传送到ret这个指针上.

线程锁

线程锁(Thread Lock)是一种同步机制,主要用于解决多线程访问共享资源时可能出现的并发问题。

#include <iostream>
#include <unistd.h>
#include <pthread.h>
int ticket = 10000;
void *ThreadRoutine(void *args)
{const char *threadname = (const char *)args;while (ticket > 0){std::cout << "i am : " << threadname << "getticket:" << ticket-- << std::endl;}return args;
}
int main()
{pthread_t tid1;pthread_create(&tid1, nullptr, ThreadRoutine, (void *)"thread 1");pthread_t tid2;pthread_create(&tid2, nullptr, ThreadRoutine, (void *)"thread 2");pthread_t tid3;pthread_create(&tid3, nullptr, ThreadRoutine, (void *)"thread 3");pthread_t tid4;pthread_create(&tid4, nullptr, ThreadRoutine, (void *)"thread 4");void *ret1 = nullptr;void *ret2 = nullptr;void *ret3 = nullptr;void *ret4 = nullptr;pthread_join(tid1, &ret1);pthread_join(tid2, &ret2);pthread_join(tid3, &ret3);pthread_join(tid4, &ret4);std::cout << "i am main thread,Finish" << std::endl;return 0;
}

如以上代码,我们模拟一个多个线程抢车票的的进程。代码中设计当车票为零时,则退出while循环,并且退出函数,退出线程。
但经过我们多次试验发现图下现象:
在这里插入图片描述
票数竟会变成负数?
假设ticket值为1当一个线程已经进入while循环内,但对于ticket值并没有做出改变,此时另一个线程就会用相同的ticket值也进入了while循环,两个进程又都进行了减减操作,导致ticket值变为-1.
这时就要用上锁,来保证多线程防止同时访问共享资源。
代码实现:

#include <iostream>
#include <unistd.h>
#include <pthread.h>
int ticket = 10000;
pthread_mutex_t lock;//全局锁
void *ThreadRoutine(void *args)
{const char *threadname = (const char *)args;pthread_mutex_lock(&lock);//上锁while (ticket > 0){std::cout << "i am : " << threadname << "getticket:" << ticket-- << std::endl;}pthread_mutex_unlock(&lock);//解锁return args;
}
int main()
{pthread_mutex_init(&lock, nullptr);pthread_t tid1;pthread_create(&tid1, nullptr, ThreadRoutine, (void *)"thread 1");pthread_t tid2;pthread_create(&tid2, nullptr, ThreadRoutine, (void *)"thread 2");pthread_t tid3;pthread_create(&tid3, nullptr, ThreadRoutine, (void *)"thread 3");pthread_t tid4;pthread_create(&tid4, nullptr, ThreadRoutine, (void *)"thread 4");void *ret1 = nullptr;void *ret2 = nullptr;void *ret3 = nullptr;void *ret4 = nullptr;pthread_join(tid1, &ret1);pthread_join(tid2, &ret2);pthread_join(tid3, &ret3);pthread_join(tid4, &ret4);std::cout << "i am main thread,Finish" << std::endl;return 0;
}

通过给区间上锁,来防止多线程同时访问资源。

死锁

在这里插入图片描述
如上图,线程A,线程B,倘若线程A中申请到了LOCK1,而线程B中申请到了LOCK2,线程A等待LOCK2的释放,线程B等待LOCK1的释放,就会导致互相等待,两个线程都进行不下去,导致死锁
如何避免:

避免嵌套锁:尽量不在持有锁的同时请求另一个锁。如果必须这样做,确保加锁的顺序在所有线程中都是一致的。
保持锁的顺序一致:多个线程在尝试获取多个锁时,总是以相同的顺序请求它们。这可以防止循环等待条件的发生,即线程A等待线程B释放锁,而线程B又在等待线程A释放另一个锁。
使用超时机制:在尝试获取锁时设置超时。如果线程不能在规定的时间内获得锁,它将放弃并稍后重试。这可以防止线程无限期地等待,从而增加了系统的灵活性。
减少锁的粒度:尽量只锁定需要保护的最小资源范围。例如,如果你可以只锁定一个数据结构的一部分而不是整个数据结构,那么这将减少死锁的可能性。
避免长时间持有锁:尽量缩短持有锁的时间。这意味着你应该在获得锁后尽快完成你的工作并释放锁。

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

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

相关文章

支持视频切片的开源物联网平台

软件介绍 MzMedia开源视频联动物联网平台是一个简单易用的系统,该平台支持主流短视频平台&#xff08;如抖音、快手、视频号&#xff09;的推流直播功能&#xff0c;同时提供视频切片等功能。系统后端采用Spring Boot&#xff0c;前端采用Vue3和Element Plus&#xff0c;消息服…

计算机毕业设计python校园二手交易系统aqj3i-

什么叫三层架构呢&#xff1f;指的是表示层、组件层、数据访问层。组件层是双层架构没有的&#xff0c;它的加入&#xff0c;把复杂的问题分解得更简单、明了&#xff0c;通过组件层&#xff0c;实现控制数据访问层&#xff0c;这样达到功能模块易于管理、易于访问等目的&#…

<MySQL> 数据库基础

目录 一、数据库概念 &#xff08;一&#xff09;什么是数据库 &#xff08;二&#xff09;数据库存储介质 &#xff08;三&#xff09;常见数据库 二、数据库基本操作 &#xff08;一&#xff09;连接数据库 &#xff08;二&#xff09;使用数据库 &#xff08;三&…

如何安全可控地进行内外网跨网络传输文件?

跨网络传输文件通常指的是在不同的网络环境之间移动数据&#xff0c;这在现代企业运营中非常常见。随着网络技术的高速发展&#xff0c;为了有效地保护内部的核心数据资产&#xff0c;企业一般会采用内外网隔离的策略。在进行内外网跨网络传输文件时&#xff0c;需要考虑多种因…

车载测试和传统测试有什么区别

搞清楚车载测试和传统应用测试的区别,就可以大胆冲冲冲! 车载测试随着市场的需求量增加又火来一波,一直’遥遥领先’的我们一定要告诉大家一个事实:车载测试和传统的应用测试是有很大区别的. 测试对象不一样 传统测试:测试的对象无非就是各种应用,比如电脑端的web系统(使用浏…

在Ubuntu 24.04 LTS (Noble Numbat)上安装nfs server以及nfs client

在Ubuntu 24.04 LTS (Noble Numbat)上,我使用的是最小化安装, 当然server版本的Ubuntu在安装的时候可能会有网络不通的问题,解决办法见如下文章: ubuntu 24.04 server 仅NAT模式上网设置静态IP设置-CSDN博客文章浏览阅读489次,点赞9次,收藏3次。在Ubuntu 24.04 上设置网…

3kCTF2021 echo klibrary

文章目录 前言echoklibrary 前言 今天状态不好&#xff0c;很多事情都不想干&#xff0c;就做一做简单的题目 echo 内核版本&#xff1a;v5.9.10smap/smep/kaslr 开启modprobe_path 可写 题目给了源码&#xff0c;非常简单就是无限次的任意地址读写&#xff1a; #include …

docker(四):数据卷

数据卷 卷的设计目的就是数据的持久化&#xff0c;完全独立于容器的生存周期&#xff0c;因此Docker不会在容器删除时删除其挂载的数据卷。 1、docker run docker run -it --privilegedtrue -v /宿主机绝对路径目录:/容器内目录 镜像名2、挂载注意事项 --privilegedtru…

YOLO数据集制作(二)|json文件转txt验证

以下教程用于验证转成YOLO使用的txt格式&#xff0c;适用场景&#xff1a;矩形框&#xff0c;配合json格式文件转成YOLO使用的txt格式脚本使用。 https://blog.csdn.net/StopAndGoyyy/article/details/138681454 使用方式&#xff1a;将img_path和label_path分别填入对应的图…

网络 | 应用层-websocket协议概述与握手过程解析

背景&#xff1a;这里为了实现消息实时传输决定引入websocket协议。 不管是发送消息还是接收消息&#xff0c;都需要实时传输&#xff0c;张三发给李四&#xff0c;李四立马就能收到&#xff0c;基于HTTP实现是有些困难的。 但轮询方式也带来了一些问题 1、消耗更多系统资源&…

Nacos 入门篇---内存注册表:高并发下如何保证注册表读写并发冲突 ?(五)

一、引言 本章来讲解Nacos注册表是如何进行写入数据的&#xff5e; 二、目录 目录 一、引言 二、目录 三、服务注册源码内容回顾 客户端源码回顾&#xff1a; 服务端源码回顾&#xff1a; 四、Nacos 注册表结构详解 五、写时复制概念 六、Nacos服务注册写入注册表源…

Lombok注解详解

文章目录 注解详解lombok包下注解汇总- Getter- Setter- ToString- EqualsAndHashCode- Data- Value- NonNull- NoArgsConstructor- AllArgsConstructor- RequiredArgsConstructor- Builder- Synchronized- Cleanup- Singular- Generated- SneakyThrows- val- var experimental…

面试八股之Redis篇2——redis分布式锁

&#x1f308;hello&#xff0c;你好鸭&#xff0c;我是Ethan&#xff0c;一名不断学习的码农&#xff0c;很高兴你能来阅读。 ✔️目前博客主要更新Java系列、项目案例、计算机必学四件套等。 &#x1f3c3;人生之义&#xff0c;在于追求&#xff0c;不在成败&#xff0c;勤通…

互联网引流艺术:精准获客的黄金法则

在如今这个信息爆炸的时代&#xff0c;互联网引流不再是简单地发布广告和等待潜在客户的到来。它变成了一门需要策略、技巧和持续创新的艺术。作为一位资深的互联网营销从业者&#xff0c;我深知精准推广的重要性&#xff0c;以及它在帮助企业获得理想客户中的关键作用。以下是…

用HAL库改写江科大的stm32入门例子_1、按键控制led灯

使用STM32CubeMX生成Keil工程并完成流水灯 ,就不重复了&#xff1a;直接参考&#xff1a; 使用STM32CubeMX生成Keil工程并完成流水灯 进入主题&#xff0c;用中断实现&#xff0c;操作步骤如下&#xff1a; 1 如下图设置PB11 管脚 2 设置PB11为下降沿中断&#xff1a; 3 PA…

探索设计模式的魅力:权力集中,效率提升,中心化模式的优势与挑战

​&#x1f308; 个人主页&#xff1a;danci_ &#x1f525; 系列专栏&#xff1a;《设计模式》 &#x1f4aa;&#x1f3fb; 制定明确可量化的目标&#xff0c;坚持默默的做事。 ✨欢迎加入探索中心化模式之旅✨ 大家好啊&#xff01;&#x1f44b; 这次我们要聊的是IT界一…

AI绘画动漫转真人详细教程

从小到大&#xff0c;我们看过的动漫、玩过的游戏有很多很多 但我们会发现里面的角色或者人物都是二次元的 我就会好奇这些动漫人物在现实中会长什么样 而现在&#xff0c;我们通过AI绘画竟然就能还原出来他们现实中的样子 除了动漫角色和游戏人物&#xff0c;古代的画像、经典…

day11-StreamFile

1.Stream流 1.1 体验Stream流 需求&#xff1a;按照下面的要求完成集合的创建和遍历 创建一个集合&#xff0c;存储多个字符串元素 把集合中所有以"杨"开头的元素存储到一个新的集合 把"杨"开头的集合中的长度为3的元素存储到一个新的集合 遍历上一步得到…

【笔记】从零开始做一个男性人体的流程/躯干篇(超级详细)

躯干整体 大体 1.创建一个正方体&#xff0c;摆好位置 2.实例呀啥的都搞好 3.胸部它是一个前窄后宽的结构 斜方肌 臀部 1.臀部是前宽后窄的结构 2.我们再去侧面调整以下 胸椎向上倾斜&#xff0c;盆骨向下倾斜。脊椎是s形的 3.真实的身体没有这么方正&#xff0c;所以微调…

第四步->手撕spring源码之bena注入实现和依赖

本步骤目标 上述几个步骤 定义和注册Bean (opens new window)、实例化Bean (opens new window)&#xff0c;按照是否包含构造函数实现不同的实例化策略 (opens new window)&#xff0c;那么在创建对象实例化这我们还缺少什么&#xff1f;其实还缺少一个关于类中是否有属性的问题…