【Linux】线程控制|POSIX线程库|多线程创建|线程终止|等待|线程分离|线程空间布局
目录
编辑
POSIX线程库
多线程创建
独立栈结构
获取线程ID
pthread_self
线程终止
return终止线程
pthread_exit
pthread_cancel
线程等待
退出码问题
线程分离
测试
线程ID及地址空间布局
编辑
POSIX线程库
pthread线程库是 POSIX线程库的一部分,POSIX线程库也叫原生线程库;
遵守 POSIX标准:与线程有关的函数构成了一个完整的系列,绝大多数函数的名字都是以“pthread_”开头的
要使用这些函数库,要通过引入头文<pthread.h>
链接这些线程函数库时要使用编译器命令的 “-lpthread” 选项;成功返回0,失败返回-1
多线程创建
主线程创建一批线程;
没有在循环创建中添加sleep();
void* handler(void* arg)
{const char* name = (const char*)arg;while(true){cout<<"new thread sucess name:"<<name<<endl;sleep(1);}
}int main()
{pthread_t tid;char namebuff[64];for(int i = 0;i<5;i++) {//格式化snprintf(namebuff,sizeof(namebuff),"%s:%d","thread:",i+1);pthread_create(&tid,nullptr,handler,namebuff); //}while(true){cout << "new thread create success, I am main thread" << endl;sleep(1);}return 0;
}
观察发现,线程编号不是我们预期的从1.2.3..开始的,而是到了最后一个线程名字;
而且线程确实创建出来了;
添加循环创建时sleep()函数 ;将数组地址改为拷贝
//把参数封成结构体
class ThreadData
{
public:pthread_t tid;char namebuffer[64];
};void* start_routine(void* args)
{ThreadData* td = static_cast<ThreadData*>(args);//static_cast 安全的进行强制类型转换,C++11int cnt = 10;while(cnt){cout << "cnt:" << cnt-- << " &cnt:" << &cnt << endl;sleep(1);}delete td;return nullptr;
}int main()
{
#define NUM 10//创建一批线程for(int i = 0; i < NUM; i++){ThreadData* td = new ThreadData();//每次循环new的都是一个新对象snprintf(td->namebuffer, sizeof(td->namebuffer), "%s:%d", "thread", i+1);//i+1 使线程下标从1开始pthread_create(&td->tid, nullptr, start_routine, td);}//主线程while(1){cout << "new thread create success, name:main thread" << endl;sleep(1);}return 0;
}
- 主线程创建新线程太快了,新线程都没有机会运行,主线程就把10个新线程创建完毕了,
- 而传参namebuffer传过去的是 缓冲区namebuffer的起始地址,
- 第十个线程创建完成之后,缓冲区的内容都被第十个线程的编号内容覆盖了,所以第一次现象线程的编号都是 10
独立栈结构
线程栈主要用于存储线程的局部变量、函数参数以及调用堆栈。当一个线程开始执行时,它的栈空间会被初始化,并且随着线程的执行,栈空间会被动态地扩展或收缩。
//把参数封成结构体
class ThreadData
{
public:pthread_t tid;char namebuffer[64];
};void* start_routine(void* args)
{ThreadData* td = static_cast<ThreadData*>(args);//static_cast 安全的进行强制类型转换,C++11int cnt = 10;while(cnt){cout << "cnt:" << cnt-- << " &cnt:" << &cnt << endl;sleep(1);}delete td;return nullptr;
}int main()
{
#define NUM 10//创建一批线程for(int i = 0; i < NUM; i++){ThreadData* td = new ThreadData();//每次循环new的都是一个新对象snprintf(td->namebuffer, sizeof(td->namebuffer), "%s:%d", "thread", i+1);//i+1 使线程下标从1开始pthread_create(&td->tid, nullptr, start_routine, td);}//主线程while(1){cout << "new thread create success, name:main thread" << endl;sleep(1);}return 0;
}
在函数内部定义的变量叫局部变量,具有临时性,在多线程的情况下依旧适用,因为每个线程都有自己的独立栈结构
获取线程ID
常见获取线程ID的方式有两种:
- 创建线程时通过输出型参数获得
- 通过调用pthread_self函数获得
pthread_self
void* start_routine(void* args)
{//安全转换std::string name = static_cast<const char*>(args);while(true){std::cout<<name<<" running ...,ID: "<<pthread_self()<<std::endl;sleep(1);}
}int main()
{pthread_t thread_id;//创建一个线程pthread_create(&thread_id,nullptr,start_routine,(void*)"thread 1:");//打印一下主线程的IDwhile(true){std::cout<<"main thread"<<" ID: "<<pthread_self()<<std::endl;sleep(1);}// 等待子线程结束pthread_join(thread_id, NULL);return 0;
}
线程终止
如果需要只终止某个线程而不终止整个进程
可以有三种方法:
- 从线程函数return。这种方法对主线程不适用,从main函数return相当于调用exit,整个进程退出
- 线程可以调用 pthread_ exit 终止自己
- 一个线程可以调用 pthread_ cancel 终止同一进程中的另一个线程
return终止线程
在多线程程序中,return关键字的使用有所不同
当非主线程时,仅表示该线程将终止其执行;在main函数中使用return则意味着整个进程将退出,这会导致进程的所有资源被释放。
用例
主线程创建多个新线程后,休眠2秒,然后进行return,那么整个进程也就退出了
class ThreadData
{
public:pthread_t tid;char namebuffer[64];
};void* start_routine(void* args)
{ThreadData* td = static_cast<ThreadData*>(args);//static_cast 安全的进行强制类型转换,C++11int cnt = 10;while(cnt){cout << "new thread create success, name:" << td->namebuffer << " cnt:" << cnt-- << endl;sleep(1);}delete td;return nullptr;
}int main()
{
#define NUM 3//创建一批线程for(int i = 0; i < NUM; i++){ThreadData* td = new ThreadData();snprintf(td->namebuffer, sizeof(td->namebuffer), "%s:%d", "thread", i+1);//i+1 使线程下标从1开始pthread_create(&td->tid, nullptr, start_routine, td);}//主线程cout << "new thread create success, name:main thread" << endl;sleep(2);//主线程两秒后退出return 0;
}
如果非主线程执行到return,仅代表该线程结束,线程退出
pthread_exit
函数终止线程
- exit 是用来终止进程的,任何一个执行流调用 exit,都会使整个进程退出;
- pthread_exit函数的功能就是终止线程;
注意
当pthread_exit()和return时,如果返回值是一个指针,该指针指向的内存空间应该是全局的或者malloc分配的(new 本质也是malloc),防止后面有其它线程通过该指针访问出错;
当然,为了避免潜在威胁,最好确保返回的指针是指向全局,malloc开辟的。这样可以确保即使线程退出,其他线程仍然可以安全地访问这些内存。不能在线程函数的栈上分配,因为当其它线程得到这个返回指针时线程函数已经退出了 ;
pthread_cancel
函数取消线程
// 定义一个用于存储线程数据的类
class ThreadData
{
public:int number; // 线程编号pthread_t tid; // 线程IDchar namebuffer[64]; // 缓冲区,用于存储线程名称
};// 线程函数
void* start_routine(void* args)
{ThreadData* td = static_cast<ThreadData*>(args);int cnt = 10;while (cnt > 0){cout << td->namebuffer << " cnt:" << cnt-- << endl;sleep(1);}// 使用 pthread_exit 返回线程编号pthread_exit((void*)td->number);
}int main()
{vector<ThreadData*> threads; // 用于存储线程数据的向量
#define NUM 5 // 定义要创建的线程数量for (int i = 0; i < NUM; i++) // 创建一批线程{ThreadData* td = new ThreadData(); // 创建 ThreadData 实例td->number = i + 1; // 设置线程编号snprintf(td->namebuffer, sizeof(td->namebuffer), "%s:%d", "thread", td->number);pthread_create(&td->tid, nullptr, start_routine, td);threads.push_back(td); // 把每个线程的信息 push 到 threads 向量中}// 主线程for (auto& iter : threads){// 输出创建成功的线程信息cout << "create thread: " << iter->namebuffer << " : " << iter->tid << " success" << endl;}sleep(5); // 主线程休眠5秒// 取消线程for (auto& iter : threads){// 取消线程pthread_cancel(iter->tid);cout << "pthread_cancel: " << iter->namebuffer << endl;}// 等待线程结束for (auto& iter : threads){void* ret = nullptr;int n = pthread_join(iter->tid, &ret); // 等待线程结束,并获取线程返回值// 断言assert(n == 0);// 输出线程结束的信息和线程退出时返回的值cout << "join: " << iter->namebuffer << " success, thread_exit_code: " << (long long)ret << endl;delete iter; // 释放 ThreadData 实例}// 主线程退出cout << "main thread quit" << endl;return 0;
}
一个线程被取消,它的退出码是 -1
线程等待
线程跟进程一样,创建后也需要主线程等待回收,如果主线程不对新线程进行等待,如果主线程不对新创建的线程进行等待,那么这个新线程的资源将不会被及时回收。这会导致类似“僵尸进程”的问题,也就是内存泄漏 ;
- 已经退出的线程,其空间没有被释放,仍然在进程的地址空间内。
- 创建新的线程不会复用刚才退出线程的地址空间
退出码问题
- void* retval 和 void** retval 有什么关系??
- 线程函数start_routine函数的返回值类型也是 void*, start_routine函数的返回值返回到哪里??
- 我们怎么获取线程的退出码,即线程的返回值??
pthread_join函数的参数 void** retval 是一个输出型参数,用来获取线程函数结束时,返回的退出结果
void** retval 是用来获取线程函数返回的退出结果,因为线程函数的返回值是 void*,所以需要用 void** 来接受 void*
注意:线程函数返回的退出结果是返回在线程库当中,参数 void** retval 需要去线程库里面接受才可以返回
线程分离
新创建的线程是 joinable(可以被等待)的。这意味着线程退出后,需要对其执行 pthread_join 操作来释放资源,否则这些资源将不会被释放,从而可能导致系统资源泄漏。如果不关心线程的返回值的话,线程等待pthread_join是一种负担;
可以使用 pthread_detach来分离线程。分离后的线程会在退出时自动释放其资源,无需主线程进行 pthread_join操作。
测试
// 将线程ID转换为字符串
string changeID(const pthread_t& thread_id)
{char tid[128];// 将线程ID格式化为十六进制字符串snprintf(tid, sizeof(tid), "0x%x", thread_id);return tid;
}// 线程函数
void* start_routine(void* args)
{string threadname = static_cast<const char*>(args);int cnt = 5;while (cnt > 0){// 输出线程名称和线程ID cout << threadname << " running..., threadID:" << changeID(pthread_self()) << endl;sleep(1);cnt--;}// 线程函数结束return nullptr;
}int main()
{pthread_t tid;// 创建一个新线程pthread_create(&tid, nullptr, start_routine, (void*)"thread 1");// 分离线程pthread_detach(tid); // 分离线程后,线程将在退出时自动释放资源// 线程默认是 joinable 的,一旦分离,就不允许再使用 pthread_join// pthread_join(tid, nullptr); // 这里如果尝试使用 pthread_join 会引发错误// 获取主线程IDstring mainID = changeID(pthread_self()); // 主线程IDwhile (1){// 输出主线程的信息 由于新线程已经被分离,其ID实际上已经不再有效,只是为了展示。cout << "main running..., mainID:" << mainID << ", new threadID:" << changeID(tid) << endl;sleep(1);}return 0;
}
线程ID及地址空间布局
pthread_create函数会产生一个线程ID,存放在第一个参数指向的地址中。该线程ID和前面说的线程ID不是一回事。
前面讲的线程ID属于进程调度的范畴。因为线程是轻量级进程,是操作系统调度器的最小单位,所以需要一个数值来唯一表示该线程。
pthread_create函数第一个参数指向一个虚拟内存单元,该内存单元的地址即为新创建线程的线程ID,属于NPTL线程库的范畴。线程库的后续操作,就是根据该线程ID来操作线程的。
用户级线程:线程ID值就是库中结构体(TCB)对象的地址
相关文章:
![](https://i-blog.csdnimg.cn/direct/85c2e96f079846eeb5ae8a2ea88bfa34.gif)
【Linux】线程控制|POSIX线程库|多线程创建|线程终止|等待|线程分离|线程空间布局
目录 编辑 POSIX线程库 多线程创建 独立栈结构 获取线程ID pthread_self 线程终止 return终止线程 pthread_exit pthread_cancel 线程等待 退出码问题 线程分离 测试 线程ID及地址空间布局 编辑 POSIX线程库 pthread线程库是 POSIX线程库的一部分…...
![](https://img-blog.csdnimg.cn/img_convert/4f004f20a1babe1a71371935f56d251e.png)
JimuReport 积木报表 v1.8.0 版本发布,开源可视化报表
项目介绍 一款免费的数据可视化报表工具,含报表和大屏设计,像搭建积木一样在线设计报表!功能涵盖,数据报表、打印设计、图表报表、大屏设计等! Web 版报表设计器,类似于excel操作风格,通过拖拽完…...
![](https://img-blog.csdnimg.cn/img_convert/03959b394bd26a3ffb5787f56522977c.png)
性能优化理论篇 | swap area是个什么东西
我们知道每台计算机的内存(RAM)都是有限的,而我们的应用程序需要加载到内存才能被运行,如果一台机器运行多个应用程序时,内存可能会耗尽。Linux 系统中的“交换空间(也称为交换分区)”可以帮助缓…...
![](https://www.ngui.cc/images/no-images.jpg)
Photoshop (PS)下载安装win/mac版
目录 一、概述 下载 二、安装步骤 三、使用教程 四、快捷键汇总 一、概述 Adobe Photoshop,简称“PS”,是由Adobe Systems开发和发行的图像处理软件。它主要处理以像素所构成的数字图像,涵盖了诸多领域,如图像编辑、图像合成…...
![](https://i-blog.csdnimg.cn/direct/d642d4151b204e2a8541e7c514be8d62.png)
初识redis:Set类型
Set有很多种含义,比如集合,比如设置(和get相对应)。 在这里我们说的set是指的redis中的集合,并且这里的集合是无序的,和之前的list是对应的。 List : [1,2,3] 和 [2,1,3] 是两个不同的listSe…...
![](https://www.ngui.cc/images/no-images.jpg)
Qt 设置QTableView的某列或某行只读
我的做法是实现一个委托(delegate),然后把它设给你要只读的列或行 class ReadOnlyDelegate: public QItemDelegate {public:ReadOnlyDelegate(QWidget *parent NULL):QItemDelegate(parent){}QWidget *createEditor(QWidget *parent, const…...
![](https://www.ngui.cc/images/no-images.jpg)
这么多焊工证,我该拥有那种焊工证呢?
焊接与切割作为特种作业工作,国家根据不同的环境和作业特点,从事焊接工作的焊工需要持证上岗工作,那么是不是焊接就只有一种工作证呢?如果不是,焊工所在的工作岗位需要什么样的焊工证,如何取得焊工证&#…...
![](https://www.ngui.cc/images/no-images.jpg)
HttpWebRequest访问https请求被中止: 未能创建 SSL/TLS 安全通道
编写代码,使用HttpWebRequest访问网页,错误提示: 未能创建 SSL/TLS 安全通道 页面本来是Http网址,突然转换为https网址,所以遇到这个问题也算正常。将解决方法记录下来给需要的人。 //只需要在 HttpWebRequest 发送…...
![](https://www.ngui.cc/images/no-images.jpg)
响应式Web设计:纯HTML和CSS的实现技巧
文章目录 响应式Web设计:纯HTML和CSS的实现技巧一、响应式Web设计概述二、实现响应式设计的技巧1. 使用媒体查询(Media Queries)2. 使用弹性布局(Flexbox)3. 使用网格布局(CSS Grid)4. 使用相对…...
![](https://www.ngui.cc/images/no-images.jpg)
linux centos 防火墙常用命令
1、开放端口 firewall-cmd --zonepublic --add-port80/tcp --permanent 1 2、查看某端口是否开放 firewall-cmd --query-port80/tcp 1 3、查看端口开启列表 firewall-cmd --list-port 1 4、重启防火墙 firewall-cmd --reload 1 5、关闭防火墙 systemctl stop firewalld.se…...
![](https://img-blog.csdnimg.cn/img_convert/385163fd29678b6c54c6c575a461560f.jpeg)
iOS18 Beta7 最终测试版推送:苹果的又一次技术飞跃
苹果公司以其一贯的创新精神和对完美的追求,再次引领了科技界的新潮流。今天,我们聚焦于苹果最新推送的iOS18 Beta7最终测试版,这一版本不仅是苹果软件更新的里程碑,更是用户体验的一次重大升级。 最终测试版的亮相 在众多果粉的…...
![](https://i-blog.csdnimg.cn/direct/19876228fb2744d0803f5b97e4960592.png)
超详细!2024叉车证办理及考试流程详解!速看!
根据《特种设备安全监察条例》规定:叉车操作员必须经专业培训和考核,取得地、市级以上质量技术监督行政部门颁发的作业人员资格证书,方可从事该项工作。叉车驾驶证是属于特种车辆驾驶证,要到全国市级质量监督局指定叉车训练基地报…...
![](https://i-blog.csdnimg.cn/direct/6793c55dd25b4c57ba724fe3d64e6420.png)
浅谈监听器插件之SSHMon Samples Collector
浅谈监听器插件之SSHMon Samples Collector SSHMon Samples Collector 是 JMeter 的一个强大插件,它允许用户通过 SSH 协议远程监控目标服务器的资源利用率,为性能测试提供实时的系统性能数据。这对于评估应用在高负载条件下的表现以及定位性能瓶颈至关…...
![](https://img-blog.csdnimg.cn/img_convert/c5f314b6282c6637c560dca919d4e683.png)
R语言论文插图模板第7期—分组散点图
在之前的文章中,分享过R语言折线图的绘制模板: 柱状图的绘制模板: 本期再来分享一下散点图(分组)的绘制方法。 先来看一下成品效果: 特别提示:本期内容『数据代码』已上传资源群中,…...
![](https://www.ngui.cc/images/no-images.jpg)
设计模式 抽象工厂方法模式
定义与特点 工厂方法模式(FactoryMethod),定义一个创建产品对象的工厂接口,让工厂子类决定实例化那一个产品类。工厂方法使一个类的实例化延迟到其子类。 我们把被创建的对象称为“产品”,把创建产品的对象称为“工厂…...
![](https://i-blog.csdnimg.cn/direct/f4597425d8ea4743aead088c4792a8c7.png)
PowerShell 一键配置IP
前言 实现一键更改Windows 网卡IP,子网,网关,dns,重命名网卡,获取的接口索引名称,获取接口名称,刷新组策略,刷新系统,脚本可重复配置,,以下环境我是两个网卡配置IP 前提条件 开启wmi,配置网卡,参考 创建更改网卡脚本 实验环境,两个网卡,清除默认,重命名(配置)…...
![](https://img-blog.csdnimg.cn/img_convert/aa0d6ccf4f3c6f683605085d7fda3416.png)
【Pyhthon读取 PDF文件表格 ,转为 CSV/TSV/JSON文件】
tabula-py tabula-py 是一个将 PDF 表格转换为 pandas DataFrame 的工具。 tabula-py 是 tabula-java 的包装器,需要您的机器上有 java。 tabula-py 还允许您将 PDF 中的表格转换为 CSV/TSV 文件。 tabula-py 的 PDF 提取准确度与 tabula-java 或 tabula app 相…...
![](https://i-blog.csdnimg.cn/direct/5959e9683fd54475999307dd95260d45.png)
<数据集>商品条形码识别数据集<目标检测>
数据集格式:VOCYOLO格式 图片数量:3748张 标注数量(xml文件个数):3748 标注数量(txt文件个数):3748 标注类别数:1 标注类别名称:[Barcode] 序号类别名称图片数框数1Barcode37484086 使用标注工具&am…...
![](https://i-blog.csdnimg.cn/direct/644d04718c7b46b2aefc7a4f724bda85.jpeg)
无人机系统的关键技术
一、飞控系统:是无人机完成整个飞行过程的关键,决定了无人机的飞行性能和稳定性。 二、导航系统:提供无人机所需的位置、速度和飞行姿态等信息,引导无人机按照指定航线飞行。 三、动力系统:提供飞行动力,…...
![](https://i-blog.csdnimg.cn/direct/d2447135075d4af585fca8b4da07964f.png)
(24)(24.4) MultiWii/DJI/HDZero OSD (version 4.2 and later)(一)
文章目录 前言 1 基于遥测的OSD 前言 ArduPilot 使用基于MSP(MultiWii 串行协议)的协议支持多种类型的屏幕显示(OSD): 基于 MSP 遥测的操作系统,如大疆 FPV 护目镜 V1/V2、大疆护目镜 RE、FatShark Byte…...
![](https://i-blog.csdnimg.cn/direct/5a437c85b35943cdba3bee1796a50d24.png)
HiveSQL:提取json串内容——get_json_oject和json_tuple
提取json串中内容,json格式示例如下 方法(运行环境Hive) get_json_objectjson_tuple get_json_object select json_data,get_json_object(json_data,$.price) as price -- 取第一层,get_json_object(json_data,$.inquiry_params) as in…...
![](https://www.ngui.cc/images/no-images.jpg)
Clickhouse 二进制安装
下载对应版本https://github.com/ClickHouse/ClickHouse 分别选择arm64/x86_64 对应的tgz 场景是需要把ck所有文件安装指定目录,使用rpm会散布在各个目录不方便管控 下载client,common-static,server这三个 将相同的目录合并成一个(这里注意需要在l…...
![](https://www.ngui.cc/images/no-images.jpg)
大数据开发工程师面试整理-项目经验
在面试或职业发展中,分享项目经验是展示个人能力和专业知识的重要途径。大数据开发工程师的项目经验通常涉及数据处理、系统设计、性能优化以及与团队协作等多个方面。以下是如何有效地展示大数据项目经验的指导,以及一个示范性的项目经验描述。 1. 结构化描述项目经验 ● 项…...
![](https://i-blog.csdnimg.cn/direct/8155dd4d4f28468685e94a5a2125e827.png)
游戏引擎详解——图片
图片 图片的格式 图片文件格式pngjpg 纹理压缩格式ETC1/2PVRTCASTC 图片的属性 图片属性解释分辨率宽高像素值(pt),如:1024*1024位深度用来存储像素颜色的值,如RGBA8888,红黄蓝透明度4个维度每个8bit&…...
![](https://i-blog.csdnimg.cn/direct/5590f97d582b46fbbc5707268fc86cfe.png)
电商API数据接口在电商运营电商数据分析中的作用?
电商运营中,品牌方使用电商API数据接口可以带来众多益处,具体包括但不限于以下几点: 实时数据同步:通过API接口,品牌方可以实时获取商品库存、订单状态、价格变动等信息,保证数据的时效性和准确性ÿ…...
![](https://www.ngui.cc/images/no-images.jpg)
Java OkHttp使用(二)
文章目录 引言使用 OkHttp 发送回调其他 引言 记录一下 OkHttp 的使用;OkHttp 异步发送回调请求,增加回调失败重试。 使用 OkHttp 发送回调 /*** 回调重试类*/ Data public class CallBackRetryData {/*** 回调信息JSON*/private JSONObject bodyRequ…...
![](https://i-blog.csdnimg.cn/direct/b3ee789aa01146e2a1ef94069fc6ea15.png)
宝塔(bt.cn)面板新手小白使用中常见问题
1.新手小白-服务器正确的安装宝塔的粗略教程 购买服务器后首先是挂载磁盘再安装宝塔,步骤不要搞错,免得安全后磁盘空间不对需要挂载多免费异步,切记切记 挂载磁盘:(挂载磁盘只需一行命令即可,请根据自己的系统选择正确…...
![](https://www.ngui.cc/images/no-images.jpg)
【LeetCode:3133】数组最后一个元素的最小值(Java)
题目链接 3133. 数组最后一个元素的最小值 题目描述 给你两个整数 n 和 x 。你需要构造一个长度为 n 的 正整数 数组 nums ,对于所有 0 < i < n - 1 ,满足 nums[i 1] 大于 nums[i] ,并且数组 nums 中所有元素的按位 AND 运算结果为…...
![](https://i-blog.csdnimg.cn/direct/9d538bb6135c40e192b01d0a5b6d1bc1.png)
FCARM - Output Name not specified, please check ‘Options for Target - Utilities‘解决方法
出现这个问题的原因是导入文件时默认类型选错了,修改文件类型即可 如图右键导入文件,选择“Options for File OLED.C” 选择“File Type”,将头文件文件类型修改为“Text Documents file”,将.c文件文件类型修改为“C Sorce file” 第二方…...
![](https://i-blog.csdnimg.cn/direct/780f2e86690f4c6f9781c0266850dc17.jpeg)
自行车制造5G智能工厂工业物联数字孪生平台,推进制造业数字化
在当今这个日新月异的数字化时代,制造业正经历着前所未有的变革,自行车制造5G智能工厂工业物联数字孪生平台的兴起,无疑是这场转型浪潮中一股强劲力量。自行车制造5G智能工厂工业物联数字孪生平台的成功应用,不仅仅是技术上的突破…...
![](/images/no-images.jpg)
电子商务网站建设下载/百度搜索排名优化
有名信号量 semphore如果说信号是外部事件和进程的关联的机制的话,那么信号量就是进程、线程之间通信的机制。根据是支持不同进程之间的通信还是同一个进程内不同线程的通信,信号量可以分为有名信号量和无名信号量。顾名思义,有名信号量在建立…...
![](https://img-blog.csdnimg.cn/20201109164813709.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2h3eDgwMjc0Ng==,size_16,color_FFFFFF,t_70#pic_center)
简洁汽车配件网站模板/百度知道网页版登录入口
什么是冒泡排序? 是通过挨个的比较,将最大(或最小)数字层层交换至所有数据的开头,每冒一次,数据头部往后移一位,经过N次冒泡之后,得到最终排序!!!…...
![](/images/no-images.jpg)
灌阳县建设局门户网站/seo怎么做整站排名
文件描述符 文件描述符编辑内核(kernel)利用文件描述符(file descriptor)来访问文件。文件描述符是非负整数。打开现存文件或新建文件时,内核会返回一个文件描述符。读写文件也需要使用文件描述符来指定待读写的文件。…...
![](https://images2017.cnblogs.com/blog/521153/201710/521153-20171013224408184-1432342371.png)
潜江 网站建设/2345网止导航
spring是一个很有名的java开源框架,作为一名javaer还是有必要了解spring的设计原理和机制,beans、core、context作为spring的三个核心组件。而三个组件中最重要的就是beans组件了。 从一个简单实例来查看spring加载配置文件中的bean。 public class Stud…...
![](/images/no-images.jpg)
可以做mv 的视频网站/重庆seo霸屏
需求 每个文件需要一个loggerlog同时输出到控制台和文件log名为脚本名_日期.log 实现代码如下 from pathlib import Path import time, sys import loggingdef get_logger(log_folder: str "logs"):"""获取指定格式的logger,实现在控制…...
![](https://img-blog.csdnimg.cn/20200509144838753.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDAzNzQxNg==,size_16,color_FFFFFF,t_70)
交易网站建设具体方案/站长工具seo查询5g5g
先分享一下 Github Desktop 的下载地址吧! 在上一篇文章中已经介绍了怎么 在Mac系统上连接git和github 接着这篇文章说说,下载好了Github Desktop ,本机也连接好了git 接下来怎么使用 使用 1、打开Github Desktop 2、创建一个项目 点击…...