【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释放另一个锁。
使用超时机制:在尝试获取锁时设置超时。如果线程不能在规定的时间内获得锁,它将放弃并稍后重试。这可以防止线程无限期地等待,从而增加了系统的灵活性。
减少锁的粒度:尽量只锁定需要保护的最小资源范围。例如,如果你可以只锁定一个数据结构的一部分而不是整个数据结构,那么这将减少死锁的可能性。
避免长时间持有锁:尽量缩短持有锁的时间。这意味着你应该在获得锁后尽快完成你的工作并释放锁。
相关文章:
【linux学习】多线程(1)
文章目录 线程的概念线程与进程 线程的用法线程的创建多线程 线程的等待线程锁死锁 线程的概念 在Linux中,线程(Thread)是程序执行流的最小单位,是进程中的一个实体,负责在程序中执行代码。线程本身不拥有系统资源&…...
Leetcode 3149. Find the Minimum Cost Array Permutation
Leetcode 3149. Find the Minimum Cost Array Permutation 1. 解题思路2. 代码实现 题目链接:3149. Find the Minimum Cost Array Permutation 1. 解题思路 这一题的话就是一个动态规划的问题,不过他这个错位着实是把题目变得复杂了不少,唉…...
Python | 为列表中的元素分配唯一值
我们可以给列表中的所有数字分配一个唯一的值,重复时它会保留给它的值。这是一个非常常见的问题,在Web开发中,处理物品id时会遇到。让我们讨论一下解决这个问题的一些方法。 1. 使用enumerate() 列表解析 # initializing list test_list …...
HTML炫酷的相册
目录 写在前面 HTML简介 完整代码 代码分析 系列推荐 写在最后 写在前面 本期小编给大家带来一个炫酷的旋转相册,快来解锁属于你的独家记忆吧! HTML简介 HTML(全称为超文本标记语言)是一种用于创建网页结构和内容的标记语…...
C++笔试强训day20
目录 1.经此一役小红所向无敌 2.连续子数组最大和 3.非对称之美 1.经此一役小红所向无敌 链接 简单模拟即可。 需要注意的是: 除完之后有无余数,若有,则还可以再挨一次打。 #include <iostream> using namespace std; #define in…...
【PHP【实战项目】系统性教学】——使用最精简的代码完成用户的登录与退出
👨💻个人主页:开发者-曼亿点 👨💻 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 👨💻 本文由 曼亿点 原创 👨💻 收录于专栏:…...
Linux下的常用基本指令
基本指令 前言一、ls 指令语法功能常用选项举例注意要点关于拼接关于 -a关于文件ls与/的联用ls与根目录ls与任意文件夹ls与常用选项与路径 ls -d与ls -ldls与ll 二、pwd命令语法功能常用选项注意要点window与Linux文件路径的区别家目录 三、cd 指令语法功能举例注意要点cd路径.…...
phpstorm环境配置与应用
在 PhpStorm 中配置 PHP 开发环境及进行一些常用的应用设置涉及以下几个主要步骤: ### 1. 安装和激活 PhpStorm - **下载安装**: 访问 JetBrains 官网下载最新版本的 PhpStorm 安装包,然后按照提示进行安装。 - **激活**: 启动 PhpStorm,你可…...
【Qt 学习笔记】Qt常用控件 | 布局管理器 | 水平布局Horizontal Layout
博客主页:Duck Bro 博客主页系列专栏:Qt 专栏关注博主,后期持续更新系列文章如果有错误感谢请大家批评指出,及时修改感谢大家点赞👍收藏⭐评论✍ Qt常用控件 | 布局管理器 | 水平布局Horizontal Layout 文章编号&…...
Hive Aggregation 聚合函数
Hive Aggregation 聚合函数 基础聚合 增强聚合...
Unity 性能优化之GPU Instancing(五)
提示:仅供参考,有误之处,麻烦大佬指出,不胜感激! 文章目录 前言一、GPU Instancing使用方法二、使用GPU Instancing的条件三、GPU Instancing弊端四、注意五、检查是否成功总结 前言 GPU Instancing也是一种Draw call…...
LeetCode 138. 随机链表的复制
目录 1.原题链接: 2.结点拆分: 代码实现: 3.提交结果: 4.读书分享: 1.原题链接: 138. 随机链表的复制 2.结点拆分: ①.拷贝各个结点,连接在原结点后面; ②.处…...
【PC微信小程序点不动处理方法】
描述 在使用电脑小程序抓包的时候发现原来能点的小程序今天不能点了。就是原来有个输入车牌号的输入框点击会出现车牌号键盘,现在不行了,经过卸载安装发现不是微信的问题,是WeChatAppEx.exe 的bug。早期使用的是不带ex的都没有问题升级以后&…...
量化交易:日内网格交易策略.md
哈喽,大家好,我是木头左! 本文将详细介绍日内网格交易策略的原理,并结合Python代码示例,展示如何在掘金平台上实现这一策略。 策略原理 日内网格交易策略的核心思想是在一天的交易时间内,通过设置多个买卖…...
Ubuntu 20.04在Anaconda虚拟环境中配置PyQt4
一、创建一个虚拟环境 1 创建一个python2.7的虚拟环境: conda create -n pyqt4 numpy matplotlib python2.72 在环境中安装几个需要的包: pip install Theano pip install python-opencv3.4.0.14 pip install qdarkstyle pip install dominate二、在主…...
charts3D地球--添加航线
要在地球视角下画出海运路线图 方案 添加 globl 地球创建geo地理坐标系创建canvas对象用于承载地图世界地图this.worldChart //初始化canvas节点let cav document.createElement("canvas");this.$echarts.registerMap("world", geoJson);this.worldCha…...
变色龙还是树懒:揭示大型语言模型在知识冲突中的行为
你是知识变色龙还是树懒?我今天在ICLR学到一个很有趣的术语,叫做证据顺序(order of evidence)。 大模型RAG处理知识冲突的探讨: 在检索增强生成(Retrieval-Augmented Generation, RAG)的过程中,技术团队会将检索到的前几名文档作为证据,并提示(prompt)给大型语言模型(Large La…...
Android OpenMAX(四)OMX Core
假设我们已经写好了所有的OMX组件,有vdec、venc、adec、aenc,接下来问题来了,我们应该如何管理这些组件呢(创建、销毁)?这一篇文章我们向上一层学习OMX Core提供的标准API。 OMX Core代码位于 OMX_Core.h OMX Core在OpenMAX IL架构中的位置位于IL Client与实际的OMX组件之…...
【Linux】轻量级应用服务器如何开放端口 -- 详解
一、测试端口是否开放 1、测试程序 TCP demo 程序(可参考:【Linux 网络】网络编程套接字 -- 详解-CSDN博客) 2、测试工具 Windows - cmd 窗口 输入命令:telnet [云服务器的公网ip] [port] 二、腾讯云安全组开放端口 1、安全组设…...
git如何查看密码
git查看用户名、邮箱 git config user.name git config user.email 也可以在系统,用户文件夹下面 gitconfig查看 通常无法查看git密码,运行以下命令 git config credential.helper 查看储存的方式,如果是manage 或manage-store则说明是…...
为内部工具集成 AI 能力时如何借助 Taotoken 简化运维
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 为内部工具集成 AI 能力时如何借助 Taotoken 简化运维 在开发内部效率工具或数据分析脚本时,集成文本生成、代码补全等…...
别再折腾Yum源了!用Docker Desktop 10分钟搞定Vulhub靶场(附一键脚本)
10分钟极速搭建Vulhub靶场:Docker Desktop全攻略 在网络安全学习和渗透测试实践中,Vulhub作为开箱即用的漏洞环境集合,已经成为安全研究者的必备工具。然而,传统的Linux环境配置过程往往让初学者望而却步——复杂的Yum源配置、漫…...
告别Claude Code封号烦恼用Taotoken稳定获取Anthropic模型服务
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 告别Claude Code封号烦恼用Taotoken稳定获取Anthropic模型服务 对于依赖Claude Code进行编程辅助的开发者来说,访问的稳…...
IL-4/IL-4R信号通路及其靶向治疗研究进展
摘要白介素-4(interleukin-4, IL-4)是一种多效细胞因子,通过特异性结合细胞表面的IL-4受体(IL-4 receptor, IL-4R)发挥生物学效应。IL-4/IL-4R信号通路在特应性皮炎、哮喘及恶性肿瘤等疾病的病理过程中发挥重要作用。近…...
第十三章:R 读取 txt、csv 表格数据
数据分析的第一步永远是读取数据。真实数据通常存储在 CSV、TXT 等文件中,本章将学习如何用 R 读取外部数据文件,以及如何把分析结果导出保存。 一、数据文件常见格式 格式扩展名特点CSV.csv逗号分隔,最通用的表格格式TXT.txt制表符或自定义…...
从直流平衡到时钟恢复:深入剖析8B10B编码在高速串行链路中的核心作用
1. 8B10B编码:高速串行通信的"交通警察" 第一次接触PCIe调试时,我拿着示波器看到波形图上那些密集的跳变信号完全摸不着头脑。直到前辈指着屏幕说:"看见这些有规律的0/1跳变了吗?这就是8B10B在指挥交通。"这个…...
A-29P深度解析:100dB回音消除与AI降噪的硬件设计实战
摘要:在可视门禁、车载蓝牙、远程会议等设备中,结构空间狭小与高音量需求往往导致严重的回声和啸叫问题。本文基于A-29P纯模拟回音消除模块,深入解析其100dB消回音能力、AI降噪特性及7种硬件应用模式,为工程师提供一套无需代码的快…...
TVA智能体范式的工业视觉革命(7)
重磅预告:本专栏将独家连载系列丛书《智能体视觉技术与应用》部分精华内容,该书是世界首套系统阐述“因式智能体”视觉理论与实践的专著,特邀美国 TypeOne 公司首席科学家、斯坦福大学博士 Bohan 担任技术顾问。Bohan先生师从美国三院院士、“…...
如何用MPC-HC打造专业级影音播放体验:从安装到优化的完整指南
如何用MPC-HC打造专业级影音播放体验:从安装到优化的完整指南 【免费下载链接】mpc-hc MPC-HCs main repository. For support use our Trac: https://trac.mpc-hc.org/ 项目地址: https://gitcode.com/gh_mirrors/mpc/mpc-hc MPC-HC(Media Playe…...
Verilog数值转换:数字设计工程师必须掌握的底层规则与工程实践
1. 项目概述:为什么Verilog数值转换是数字设计的基石在数字电路设计和FPGA开发中,Verilog是我们描述硬件行为的主要语言。很多刚入行的朋友,包括我当年,都曾以为写Verilog就是写“另一种编程语言”,把C语言或Python的习…...
