【Linux】线程概念 | 线程控制
文章目录
- 👉知识补充👈
- 👉Linux线程概念👈
- 什么是线程
- Makefile
- 线程 VS 进程
- 线程的优点
- 线程的缺点
- 线程异常
- 线程用途
- 👉线程控制👈
- 线程终止
- pthread_exit 函数
- pthread_cancel 函数
- 线程 ID 的深入理解
- 在多线程的场景下进行进程替换
- 线程等待
- 线程分离
- 👉总结👈
👉知识补充👈
👉Linux线程概念👈
什么是线程
- 线程是在进程内部执行的,也就是说线程是在进程的地址空间内运行的,其是操作系统调度的基本单位。
- 进程等于内核数据结构加上该进程对应的代码和数据,内核数据结构可能不止一个 PCB,进程是承担分配系统资源的基本实体,将资源分配给线程!
- 那如何理解我们之前写的代码呢?其实我们之前学习的是只有一个执行流的进程,而今天学习的是具有多个执行流的进程(task_struct 是进程内部的一个执行流),所以这两者是不冲突的。
- 在运行队列中排队的都是 task_struct,CPU 只能看到 task_struct,CPU 根本不关系当前调度的是进程还是线程,只关心 task_struct。所以,CPU 调度的基本单位是”线程”。
- Linux 下的线程是轻量级进程,没有真正意义上的线程结构,没有为线程专门设计内核数据结构,而是通过 PCB 来模拟实现出线程的。
- Linux 并不能直接给我们提供线程相关的接口,只能提供轻量级进程的接口!在用户层实现了一套多进程方案,以库的方式提供给用户进行使用,这个库就是 pthread 线程库(原生线程库)。
知道了什么是线程,我们来学习创建线程的接口,来验证一下上面的结论!
pthread_create 函数的功能是创建一个新的线程。thread 是输出型参数,返回进程的 ID;attr 设置线程的属性,attr 为 nullptr 表示使用默认属性;start_routine 是一个函数地址,即线程启动后要执行的函数;arg 是传给线程启动函数的参数。调用成功是返回 0,错误是返回错误码。
Makefile
mythread:mythread.ccg++ $^ -o $@ -std=c++11 -lpthread
.PHONY:clean
clean:rm -f mythread
注:使用原生线程库时,必须带上 -lpthread
,告诉编译器你要链接原生线程库,否则就会产生链接错误。
// 注:一下代码是示例代码,有些许问题
#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <cstdio>
#include <string>using namespace std;void* threadRun(void* args)
{string name = (char*)args;while(1){cout << name << " id: " << getpid() << '\n' << endl;sleep(1);}return nullptr;
}int main()
{pthread_t tid[5];char name[64];for (int i = 0; i < 5; i++){snprintf(name, sizeof name, "%s-%d", "thread", i);pthread_create(tid + i, nullptr, threadRun, (void*)name);sleep(3); // 缓解传参的bug}while (true){cout << "main thread, pid: " << getpid() << endl;sleep(3);}return 0;
}
ps -aL | head -1 && ps -aL | grep mythread | grep -v grep #查找线程
将进程 16889 杀掉时,全部执行流都会终止。因为线程用的资源都是进程给的,而杀掉进程就要回收进程的资源,那么线程终止了是理所当然的。
线程是如何看到进程内部的资源的呢?
我们知道,线程的运行依赖于进程的资源,一旦进程退出,线程也会退出。那进程的哪些资源是线程之间共享的,哪些资源又是线程独自占用的呢?
进程的大多数资源都被线程所共享:
- 文件描述符表,如果一个线程打开了一个文件,那么其他的线程也能够看到。
- 每种信号的处理方式(SIG_IGN、SIG_DFL 或者自定义的信号处理函数)
- 当前工作目录
- 用户 ID 和组 ID
- 进程地址空间的代码区、共享区
- 已初始化、未初始化数据区,也就是全局变量
- 堆区一般也是被所有线程共享的,但在使用时,认为线程申请的堆空间是线程私有的,因为只有这个线程拿到这段空间的其实地址
线程独自占用的资源:
- 线程 ID
- 一组寄存器。线程是 CPU 调度的基本单位,一个线程被调度一定会形成自己的上下文,那么这组寄存器必须是私有的,才能保证正常的调度。
- 栈。每个线程都是要通过函数来完成某种任务的,函数中会定义各种临时变量,那么线程就需要有自己私有的栈来保存这些局部变量。
- 错误码 errno、信号屏蔽字、调度优先级
线程 VS 进程
为什么线程的调度切换的成本更低呢?
线程进行切换时,进程地址空间和页表是不用换的。而进程进行切换时,需要将进程的上下文,进程地址空间、页表、PCB 等都要切换。CPU 内部是有 L1 ~ L3 的 Cache,CPU 执行指令时,会更具局部性原理将内存中的代码和数据预读到 CPU 的缓存中。如果是多线程,CPU 预读的代码和数据很大可能就会被所有的线程共享,那么进行线程切换时,下一个线程所需要的代码和数据很有可能已经被预读了,这样线程切换的成本就会更低!而进程具有独立性,进行进程切换时,CPU 的 Cache 缓存的代码和数据就会立即失效,需要将新进程的代码和数据重新加载到 Cache 中,所以进程切换的成本是更高的。
进程和线程的关系如下图:
线程的优点
- 创建一个新线程的代价要比创建一个新进程小得多
- 与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多
- 线程占用的资源要比进程少很多
- 能充分利用多处理器的可并行数量
- 在等待慢速 I / O 操作结束的同时,程序可执行其他的计算任务
- 计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现
- I / O 密集型应用,为了提高性能,将 I / O 操作重叠。线程可以同时等待不同的 I / O 操作
注:线程不是创建越多越好,因为线程切换也是有成本的,并不是不需要成本。创建线程太多了,线程切换的成本有可能就是最大的成本了。
线程的缺点
- 性能损失:一个很少被外部事件阻塞的计算密集型线程往往无法与其它线程共享同一个处理器。如果计算密集型线程的数量比可用的处理器多,那么可能会有较大的性能损失,这里的性能损失指的是增加了额外的同步和调度开销,而可用的资源不变。
- 健壮性降低:编写多线程需要更全面更深入的考虑,在一个多线程程序里,因时间分配上的细微偏差或者因共享了不该共享的变量而造成不良影响的可能性是很大的,换句话说线程之间是缺乏保护的。如:一个线程对全局变量修改了,另外的线程的全局变量也会跟着修改;还有就是如果主线程挂掉了,其他线程也会跟着挂掉。
- 缺乏访问控制:进程是访问控制的基本粒度,在一个线程中调用某些操作系统函数会对整个进程造成影响。
- 编程难度提高:编写与调试一个多线程程序比单线程程序困难得多。
线程异常
- 单个线程如果出现除零,野指针问题导致线程崩溃,进程也会随着崩溃。
- 线程是进程的执行分支,线程出异常,就类似进程出异常,进而触发信号机制,终止进程。进程终止,该进程内的所有线程也就随即退出。
线程用途
- 合理的使用多线程,能提高 CPU 密集型程序的执行效率。
- 合理的使用多线程,能提高 I / O 密集型程序的用户体验(如生活中我们一边写代码一边下载开发工具,就是多线程运行的一种表现)。
👉线程控制👈
clone 函数可以创建线程或者子进程,可以设置回调函数,子进程的栈区,还有各种属性等等。除了 clone 函数,还有一个 vfork 函数。vfork 函数创建出来的子进程是和父进程共享进程地址空间的。
#include <iostream>
#include <string>
#include <unistd.h>
#include <cassert>
#include <sys/types.h>
#include <sys/wait.h>using namespace std;int globalVal = 100;int main()
{int id = vfork();// int id = fork();assert(id != -1);if(id == 0){// child processint count = 0;while(1){cout << "child process -> globalVal: " << globalVal << endl;sleep(1);++count;if(count == 5){globalVal = 200;cout << "child process change globalVal!" << endl;exit(1);}}}//waitpid(id, nullptr, 0); // 为了演示现象就不等待子进程了// parent processwhile(1){cout << "parent process -> globalVal: " << globalVal << endl;sleep(1);}return 0;
}
结论:线程谁先运行与调度器相关。线程一旦异常都有可能导致整个进程整体退出!
线程终止
如果需要只终止某个线程而不终止整个进程,可以有三种方法:
- 从线程函数 return。这种方法对主线程不适用,从main 函数 return 相当于调用 exit。
- 线程可以调用 pthread_ exit 终止自己。
- 一个线程可以调用 pthread_ cancel 终止同一进程中的另一个线程。
注:在多线程场景下,不要使用 exit 函数,exit 函数是终止整个进程的!
pthread_exit 函数
- pthread_exit 函数的功能是终止线程。
- retval:retval 不要指向一个局部变量。
- 无返回值,跟进程一样,线程结束的时候无法返回到它的调用者(自身)。
#include <iostream>
#include <pthread.h>
#include <unistd.h>using namespace std;void* threadRoutine(void* args)
{int i = 0;while(1){cout << "新线程: " << (char*)args << " running ..." << endl;sleep(1);if(i++ == 3) break;}cout << (char*)args << " quit" << endl;pthread_exit((void*)10);
}int main()
{pthread_t tid;pthread_create(&tid, nullptr, threadRoutine, (void*)"thread one");void* ret = nullptr;pthread_join(tid, &ret);cout << "ret: "<< (long long)ret << " main thread wait done... main quit too" << endl;return 0;
}
pthread_cancel 函数
pthread_cancel 函数的功能是取消一个执行中的线程。thread 是线程的 ID,调用成功是返回 0,失败是返回错误码。
#include <iostream>
#include <pthread.h>
#include <unistd.h>using namespace std;void* threadRoutine(void* args)
{int i = 0;while(1){cout << "新线程: " << (char*)args << " running ..." << endl;sleep(1);}cout << (char*)args << " quit" << endl;pthread_exit((void*)13);
}int main()
{pthread_t tid;pthread_create(&tid, nullptr, threadRoutine, (void*)"thread one");// pthread_cancel(tid); // 不要一创建线程就取消它int count = 0;while(1){cout << "main线程 running ..." << endl;sleep(2);count++;if(count == 5) break;}pthread_cancel(tid);cout << "pthread cancel tid: " << tid << endl;void* ret = nullptr;pthread_join(tid, &ret);cout << "ret: "<< (long long)ret << " main thread wait done... main quit too" << endl;return 0;
}
当一个线程被取消时,线程的退出结果是 -1(PTHREAD_CANCELED)。使用 pthread_cancel 函数的前提是线程已经跑起来了才能够取消,所以不要穿甲一个线程后就立马取消(可能刚创建的线程还没有跑起来)。一般情况下,都是用主线程来取消新线程的。如果使用新线程来取消主线程的话,这样会影响整个进程。
线程 ID 的深入理解
线程 ID 本质是一个地址!!!因为我们目前用的不是 Linux 自带的创建线程的接口,用的是 pthread 库中的接口!用户需要的是线程,而 Linux 系统只提供轻量级进程,无法完全表示线程,所以在用户和操作系统之间加了个软件层 pthread 库。操作系统承担轻量级进程的调度和内核数据结构的管理,而线程库要给用户提供线程相关的属性字段,包括线程 ID、栈的大小等等。
pthread_self 函数可以获取当前线程的 ID,既然能获得当前线程的 ID,那么线程就可以自己取消自己,但是这种方式不推荐!
线程局部存储:用 __thread 修饰全局变量带来的结果就是让每一个线程各自拥有一个全局变量,这就是线程的局部存储。
#include <iostream>
#include <pthread.h>
#include <unistd.h>using namespace std;__thread int g_val = 0;void* threadRoutine(void* args)
{while(1){cout << (char*)args << " g_val:" << g_val << " &g_val:" << &g_val << endl;g_val++;sleep(1);}return nullptr;
}int main()
{pthread_t tid;pthread_create(&tid, nullptr, threadRoutine, (void*)"thread 1");while(1){cout << "main thread" << " g_val:" << g_val << " &g_val:" << &g_val << endl;sleep(2);}pthread_join(tid, nullptr);return 0;
}
去掉 __thread 修饰后,所有线程看到的全局变量都是同一个!__thread 所有 pthread 库给 g++ 编译器的一个编译选项!
在多线程的场景下进行进程替换
#include <iostream>
#include <pthread.h>
#include <unistd.h>using namespace std;__thread int g_val = 0;void* threadRoutine(void* args)
{sleep(5);execl("/bin/ls", "ls", "-l", nullptr);while(1){cout << (char*)args << " g_val:" << g_val << " &g_val:" << &g_val << endl;g_val++;sleep(1);}return nullptr;
}int main()
{pthread_t tid;pthread_create(&tid, nullptr, threadRoutine, (void*)"thread 1");while(1){cout << "main thread" << " g_val:" << g_val << " &g_val:" << &g_val << endl;sleep(1);}pthread_join(tid, nullptr);return 0;
}
在多线程的场景下执行进程替换,那么先会将除主线程外的其它线程都终止掉,然后再进行进程替换。
线程等待
线程在创建并执行的时候,线程也是需要被等待的。如果不等待线程的话,会引起类似于进程的僵尸问题,进而导致内存泄漏。已经退出的线程,其空间没有被释放,仍然在进程的地址空间内。创建新的线程不会复用刚才退出线程的地址空间。
pthread_join 函数的功能是等待线程结束。thread
是要线程的 ID,retval 指向线程所执行的函数的返回值。调用该函数的线程> 将阻塞等待,直到 ID为 thread 的线程终止。thread 线程以不同的方法终 止,通过 pthread_join 得到的终止状态是不同的,总结如下:
- 如果 thread 线程通过 return 返回,retval 所指向的单元里存放的是 thread 线程函数的返回值。
- 如果 thread 线程被别的线程调用 pthread_ cancel 异常终掉,retval 所指向的单元里存放的是常数
PTHREAD_ CANCELED。- 如果 thread 线程是自己调用 pthread_exit 终止的,retval 所指向的单元存放的是传给 pthread_exit 的参数。
- 如果对 thread 线程的终止状态不感兴趣,可以传 nullptr 给 retval 参数。
- thread 线程函数的返回值不会考虑异常的情况,如果线程出现了异常,那么整个进程都会崩掉。注:状态寄存器是所有线程共享的。
#include <iostream>
#include <pthread.h>
#include <unistd.h>using namespace std;void* threadRoutine(void* args)
{int i = 0;while(1){cout << "新线程: " << (char*)args << " running ..." << endl;sleep(1);if(i++ == 6) break;}cout << (char*)args << " quit" << endl;return nullptr;
}int main()
{pthread_t tid;pthread_create(&tid, nullptr, threadRoutine, (void*)"thread one");pthread_join(tid, nullptr); // 默认会阻塞等待cout << "main thread wait done... main quit too" << endl;return 0;
}
线程执行的函数的返回值是返回给主线程的,主线程通过该返回值来获取线程退出的状态。
#include <iostream>
#include <pthread.h>
#include <unistd.h>using namespace std;void* threadRoutine(void* args)
{int i = 0;while(1){cout << "新线程: " << (char*)args << " running ..." << endl;sleep(1);if(i++ == 6) break;}cout << (char*)args << " quit" << endl;return (void*)10;
}int main()
{pthread_t tid;pthread_create(&tid, nullptr, threadRoutine, (void*)"thread one");void* ret = nullptr;pthread_join(tid, &ret);cout << "ret: "<< (long long)ret << " main thread wait done... main quit too" << endl;return 0;
}
线程执行的函数的返回值可以多种多样,比如返回一段堆空间的起始地址。
#include <iostream>
#include <pthread.h>
#include <unistd.h>using namespace std;void* threadRoutine(void* args)
{int i = 0;int* ret = new int[7];while(1){cout << "新线程: " << (char*)args << " running ..." << endl;sleep(1);ret[i] = i;if(i++ == 6) break;}cout << (char*)args << " quit" << endl;return (void*)ret;
}int main()
{pthread_t tid;pthread_create(&tid, nullptr, threadRoutine, (void*)"thread one");int* ret = nullptr;pthread_join(tid, (void**)&ret);for(int i = 0; i < 7; ++i)cout << ret[i] << ' ';cout << endl;cout << "ret: "<< (long long)ret << " main thread wait done... main quit too" << endl;return 0;
}
线程分离
- 默认情况下,新创建的线程是 joinable 的,线程退出后,需要对其进行 pthread_join 操作,否则无法释放资源,从而造成系统泄漏。
- 如果不关心线程的返回值,join 是一种负担。这个时候,我们可以告诉系统:当线程退出时,自动释放线程资源,这就是线程分离。
- 一般主线程时不退出的,当用户有个任务要处理,主线程就可以创建新线程来执行用户的任务,但主线程不关心任务处理的结果,那么就可以将该线程分离出去。
#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <cerrno>
#include <cstring>using namespace std;__thread int g_val = 0;void* threadRoutine(void* args)
{pthread_detach(pthread_self());while(1){cout << (char*)args << " g_val:" << g_val << " &g_val:" << &g_val << endl;g_val++;break;}pthread_exit((void*)11);
}int main()
{pthread_t tid;pthread_create(&tid, nullptr, threadRoutine, (void*)"thread 1");while(1){cout << "main thread" << " g_val:" << g_val << " &g_val:" << &g_val << endl;sleep(1);break;}int n = pthread_join(tid, nullptr);cout << "n: " << n << " error string: " << strerror(n) << endl;return 0;
}
注:joinable 和分离是冲突的,一个线程不能既是joinable 又是分离的。
如果线程被分离,但是该线程出现了异常,这样也会影响到整个进程。线程执行的是进程派发的任务,尽管线程被分离了,线程也离不开进程的资源,所以线程出现了异常也会导致进程终止。
注:C++ 11 的线程库也是调用了原生线程库的,所以在使用 C++ 的线程库时也要指定链接原生线程库。
👉总结👈
本篇博客主要讲解了什么是线程、线程和进程的区别、线程的优缺点、线程异常、线程用途以及线程控制等。那么以上就是本篇博客的全部内容了,如果大家觉得有收获的话,可以点个三连支持一下!谢谢大家!💖💝❣️
相关文章:
【Linux】线程概念 | 线程控制
文章目录 👉知识补充👈👉Linux线程概念👈什么是线程Makefile线程 VS 进程线程的优点线程的缺点线程异常线程用途 👉线程控制👈线程终止pthread_exit 函数pthread_cancel 函数线程 ID 的深入理解在多线程的场…...
OCR的评价指标和常用数据集
1.OCR任务简介 OCR(Optical Character Recognition,光学字符识别)是指对包含文本内容的图像或者视频进行处理识别,并提取其中所包含的文字及排版信息的过程。例如,一个常见的应用是将包含文档图像的不可编辑状态的 PDF 文档通过 OCR 技术识别后…...
React性能优化
三个可以优化的地方 避免过度多次渲染 组件会在以下情况下重新渲染 注意:例如组件组合的形式,<Test><Counter></Counter></Test>,即使Test发生了重新渲染,Counter也不会重新渲染。另外使用React这样的库或框架时&a…...
Django之ORM
1.ORM介绍 ORM概念 对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。 简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对…...
macos上设置windsurf命令行启动
解决方案 1. 在/usr/local/bin 目录下创建软链 sudo ln -s /Applications/Windsurf.app/Contents/Resources/app/bin/windsurf /usr/local/bin/windsurf 2. 执行source source ~/.zshrc...
一、web基础和http协议
前言 https://www.baidu.com/:URL(是一种万维网寻址网址) https://:协议,加密的http,加密的超文本传输协议,在数据传输之前要通过整数进行身份验证,验证通过才可以进行数据传输。 …...
Can‘t find variable: token(token is not defined)
文章目录 例子 1:使用 var例子 2:使用 let 或 const例子 3:异步操作你的代码中的情况 Cant find variable: tokentoken is not defined源代码 // index.jsPage({data: {products:[],cardLayout: grid, // 默认卡片布局为网格模式isGrid: tr…...
Elasticsearch ILM 故障排除:常见问题及修复
作者:来自 Elastic Stef Nestor 大家好!我们的 Elasticsearch 团队正在不断改进我们的索引生命周期管理 (index Lifecycle Management - ILM) 功能。当我第一次加入 Elastic Support 时,我通过我们的使用 ILM 实现自动滚动教程快速上手。在帮…...
1. 设计模式的由来
设计模式的灵感来自建筑师亚历山大的“设计套路”,后来被程序员借用,总结出一套“编程武功秘籍”。 20世纪90年代,四位软件工程师(被称为“四人帮”)——Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides&…...
【Excel】Excel中将日期格式转换为文本格式,并按日期显示。
【问题需求】 在使用excel进行数据导入的过程中, 有的软件要求日期列必须是文本格式。 但是直接将日期列的格式改为文本后,显示一串数字,而不按日期显示。 进而无法导入使用。 【解决方法】 使用【TXET】函数公式进行处理, 在单独…...
SpringBoot入门:如何新建SpringBoot项目(保姆级教程)
在本文中,我们将演示如何新建一个基本的 Spring Boot 项目。写这篇文章的时候我还是很惊讶的,因为我发现有些java的初学者,甚至工作10年的老员工居然并不会新建一个SpringBoot项目,所以特别出了一篇文章来教大家新建一个SpringBoo…...
什么是Layer Normalization?
一、概念 前面的文章中,我们介绍了Batch Normalization。BN的目的是使得每个batch的输入数据在每个维度上的均值为0、方差为1(batch内,数据维度A的所有数值均值为0、方差为1,维度B、C等以此类推),这是由于神…...
基于神经网络的弹弹堂类游戏弹道快速预测
目录 一、 目的... 1 1.1 输入与输出.... 1 1.2 隐网络架构设计.... 1 1.3 激活函数与损失函数.... 1 二、 训练... 2 2.1 数据加载与预处理.... 2 2.2 训练过程.... 2 2.3 训练参数与设置.... 2 三、 测试与分析... 2 3.1 性能对比.... 2 3.2 训练过程差异.... 3 四、…...
ASP.NET CORE API 解决跨域问题
环境 vs2022 .net 8 创建ASP.net Core API项目 配置跨域 编写ApiController 启动项目 得到服务器运行的 地址 在Hbuiler中创建web项目,编写代码 【运行】-【运行到浏览器】-选择一个浏览器,查看结果 正常显示 问题 如果允许所有源访问,有安全风险方…...
西安理工大学丨ChatGPT助力学术论文写作训练营-助力发表SCI一区、二区
在当今学术研究中,科研人员在撰写论文时面临诸多挑战。首先是信息量的剧增,科研人员需要快速消化新知识,筛选相关信息并清晰表达。但论文写作不仅是信息的罗列,还需要条理清晰、逻辑严密、语言精准,特别是在竞争激烈的…...
Android hid 数据传输(device 端 )
最近一直在处理hid 数据需求,简而言之就是两台设备直接可以通过usb 线互相传递数据。 项目架构 为什么Device 端要采用HID(人机接口设备)的方式发送和接收数据呢? 主要是速度快,举个例子,就是鼠标移动&am…...
Facebook 人工智能:重塑社交新未来
在数字化迅速发展的今天,人工智能(AI)已经深入了我们的生活,尤其是在社交媒体领域。Facebook作为全球最大的社交平台之一,正利用AI技术,革新其服务和用户体验,为用户打造社交互动的新未来。 首先,人工智能…...
UIlicious - 自动化端到端测试
在现代软件开发中,测试自动化已然成为产品交付质量的基石。而端到端测试(E2E),作为验证整个应用流畅运行的关键,常常是测试工作中最具挑战性的一环。这时,一款简单高效的自动化测试工具——UIlicious&#…...
PHP 与 MySQL 搭配的优势
一、PHP 与 MySQL 搭配的优势 强大的动态网页开发能力 PHP 是一种服务器端脚本语言,能够生成动态网页内容。它可以根据用户的请求、数据库中的数据等因素,实时地生成 HTML 页面返回给客户端浏览器。而 MySQL 是一个流行的关系型数据库管理系统…...
HDFS 操作命令
在现代的企业环境中,单机容量往往无法存储大量数据,需要跨机器存储。统一管理分布在 集群上的文件系统称为 分布式文件系统 。 HDFS ( Hadoop Distributed File System )是 Apache Hadoop 项目的一个子项目, Hadoo…...
第四篇:k8s 理解Service工作原理
什么是service? Service是将运行在一组 Pods 上的应用程序公开为网络服务的抽象方法。 简单来说K8s提供了service对象来访问pod。我们在《k8s网络模型与集群通信》中也说过k8s集群中的每一个Pod(最小调度单位)都有自己的IP地址,都…...
什么是TCP的三次握手?
TCP的三次握手:深入理解建立可靠连接的过程 引言 在计算机网络中,传输控制协议(TCP)是确保数据可靠传输的核心协议之一。TCP通过三次握手机制来建立一个稳定的、双向的连接,这对于确保数据的完整性和顺序至关重要。本…...
Pytorch使用手册- TorchVision目标检测微调Tutorial的使用指南(专题十二)
这篇教程的目标是对一个预训练的 Mask R-CNN 模型进行微调,应用于 Penn-Fudan 行人检测与分割数据集。该数据集包含 170 张图像,里面有 345 个行人实例,我们将通过这个教程来演示如何使用 torchvision 中的新特性,训练一个面向自定义数据集的目标检测和实例分割模型。 注意…...
Modern Effective C++条款三十五:优先考虑基于任务的编程而非基于线程的编程
C中开发者可以通过两种主要方式异步执行一个函数,如doAsyncWork()。这两种方法分别是基于线程(thread-based)和基于任务(task-based)的方式。 基于线程的方式 使用std::thread创建一个新的线程来执行doAsyncWork()函数,直接且直观,但也有其…...
lyapunov指数的绘制
有如下方程: %% 方程式 % x(n1)1y(n)-a*x(n)^2 % y(n1)b*x(n)绘制其对应的lyapunov指数。 MATLAB实现方式: clc; clearvars; close all;%% 方程式 % x(n1)1y(n)-a*x(n)^2 % y(n1)b*x(n)%% 代码 N 1000; a (0:0.001:1.4); b 0.3; na length(a…...
WPF+MVVM案例实战与特效(三十二)- 封装一个Appconfig 操作类(保留注释)
文章目录 1、概述2、ConfigHelper 的功能3、代码实现1、ConfigHelper.cs 代码2、帮助类使用4、总结1、概述 在开发 WPF 应用程序时,配置文件(如 App.config )是存储应用程序设置、连接字符串和其他运行时信息的常用方式。然而,直接操作这些配置文件可能会涉及到复杂的 XML…...
决策树:ID3、C4.5和CART特征选择方式
1 前言 该文章主要目的是记录ID3、C4.5和CART特征选择方式,这里只对决策树进行简单介绍。 决策树(Decision Tree)算法是一种有监督学习算法,它利用分类的思想,根据数据的特征构建数学模型,从而达到数据的筛…...
02 conda常用指令
目录 命令快速查找命令详细解释列出当前conda中存在的解释器环境使用指定的解释器环境创建虚拟环境激活自己创建的虚拟环境虚拟环境删除切换回主环境找到你计算机中安装的miniconda3的跟目录找到虚拟环境的目录选择需要删除的虚拟环境文件夹确认环境是否删除 补充删除虚拟环境指…...
从仪表盘探索 MongoDB 关键指标
这是 MongoDB 监控系列文章的第七篇,前面几篇文章的链接如下: MongoDB 监控(一)MongoDB 监控(二)MongoDB 监控(三)MongoDB 监控(四)MongoDB 监控(…...
Grule前端表单post后端执行grule引擎规则
Grule前端表单post后端执行grule引擎规则 编写前端表单和后端接口 编写test.go执行grule引擎规则 示例都是 go test 执行的测试代码,所以将里面的测试代码去除 由于之前 NumberExponentExample_test.go 已经验证可运行, 所以将 err 的异常处理去除 package mai…...
EasyRTC支持嵌入式智能硬件与微信小程序实时通话
基础建设如此发达的时代,各种物联网设备都越来越普及,尤其是可穿戴设备和智能家居设备的发展,而在物联网设备中,视频物联网设备又是特别受人关注的设备,因为他们具备有看得见的属性,像智能家居里面的摄像头…...
openGauss开源数据库实战十九
文章目录 任务十九 openGauss DML 语句测试任务目标实施步骤一、准备工作二、INSERT语句三、DELETE语句四、UPDATE语句五、清理工作 任务十九 openGauss DML 语句测试 任务目标 掌握DML语句的用法,包括INSERT语句、DELETE语句和UPDATE语句。 实施步骤 一、准备工作 使用Li…...
基于XML的AOP开发
AOP 为 Aspect Oriented Programming 的缩写,意思为面向切面编程。 AOP相关术语: 目标对象(Target): 你要去代理的对象,可以理解为之前很单纯的那个对象。 代理对象(Proxy): 你把你那个单纯的对象给我,…...
获取Ubuntu-22.04.1 对应的vmlinux文件
0.前言 🚀write in front🚀 🔎大家好,我是黄桃罐头,希望你看完之后,能对你有所帮助,不足请指正!共同学习交流 🎁欢迎各位→点赞👍 收藏⭐️ 留言…...
Python后端 -- 万字长文全面解析Django框架
自从2005年诞生以来,Django因其“开发速度快、安全性高”的特点迅速成为许多开发者的首选。无论是小型的个人项目,还是大型的企业应用,Django都能游刃有余地满足需求。我们将从Django的基础知识带你全面掌握Django,从基础知识到高…...
“指标管理系统”是什么?企业如何搭建指标管理系统?
在当今数字化时代,数据已成为企业决策的重要依据。然而,海量数据中如何筛选出关键指标,并对其进行有效管理,成为了众多企业面临的难题。为此,指标管理系统应运而生,它旨在帮助企业规范化定义、统一管理和高…...
Node.js简单接口实现教程
Node.js简单接口实现教程 1. 准备工作 确保您的计算机已安装: Node.js (建议版本16.x以上)npm (Node包管理器) 2. 项目初始化 # 创建项目目录 mkdir nodejs-api-tutorial cd nodejs-api-tutorial# 初始化npm项目 npm init -y# 安装必要依赖 npm install expres…...
交换机四大镜像(端口镜像、流镜像、VLAN镜像、MAC镜像)应用场景、配置实例及区别对比
在网络管理中,端口镜像、流镜像、VLAN镜像和MAC镜像都是用于监控和分析网络流量的重要技术。 端口镜像(Port Mirroring) 定义:端口镜像是将一个或多个源端口的流量复制到一个目标端口,以便于网络管理员能够监控和分析…...
服务器上的常见Linux命令教程
在管理服务器(如香港服务器)时,掌握常见的 Linux 命令 是非常重要的,它们可以帮助你高效地完成服务器管理任务,如文件操作、进程管理、用户管理、网络配置等。 以下是一个系统化的 Linux 常见命令教程,分为…...
聚合支付系统/官方个人免签系统/三方支付系统稳定安全高并发 附教程
聚合支付系统/官方个人免签系统/三方支付系统稳定安全高并发 附教程 系统采用FastAdmin框架独立全新开发,安全稳定,系统支持代理、商户、码商等业务逻辑。 针对最近一些JD,TB等业务定制,子账号业务逻辑API 非常详细,方便内置…...
【MySQL】使用 JDBC 连接数据库
文章目录 前言1. 认识 JDBC 1.1 概念1.2 好处 2. 使用 JDBC 2.1 安装数据驱动包2.2 把 jar 包导入到项目中2.3 代码编写2.4 测试结果 3. 代码优化4. 源码展示结语 前言 在 MySQL 系列中,我们介绍了很多内容,包括但不限于建库建表,增删查改等…...
深入浅出:PHP会话管理(Session 和 Cookie)
深入浅出:PHP会话管理(Session 和 Cookie) 前言 在Web开发中,会话管理是确保用户状态持续性和数据安全的关键。通过会话管理,我们可以在多个页面请求之间保持用户的登录状态、购物车信息等。PHP提供了两种主要的会话…...
Logistic Regression(逻辑回归)、Maximum Likelihood Estimatio(最大似然估计)
Logistic Regression(逻辑回归)、Maximum Likelihood Estimatio(最大似然估计) 逻辑回归(Logistic Regression,LR)逻辑回归的基本思想逻辑回归模型逻辑回归的目标最大似然估计优化方法 逻辑回归…...
【开源】A063—基于Spring Boot的农产品直卖平台的设计与实现
🙊作者简介:在校研究生,拥有计算机专业的研究生开发团队,分享技术代码帮助学生学习,独立完成自己的网站项目。 代码可以查看项目链接获取⬇️,记得注明来意哦~🌹 赠送计算机毕业设计600个选题ex…...
单链表---合并两个链表
将两个升序链表合并为一个新的升序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 struct ListNode {int val;struct ListNode* next; }; w 方法一---不使用哨兵位 我们创建一个新链表用于合并两个升序链表, 将两个链表中最小的结点依次尾插到…...
draggable插件——实现元素的拖动排序——拖动和不可拖动的两种情况处理
最近在写后台管理系统的时候,遇到一个需求,就是关于拖动排序的功能。 我之前是写过一个关于拖动表格的功能,此功能可以实现表格中的每一行数据上下拖动实现排序的效果。 vue——实现表格的拖拽排序功能——技能提升 但是目前我这边的需求是…...
vite+vue3 配置ip和端口以及自动打开浏览器
编辑文件vite.config.ts 修改前vite.config.ts文件内容 import { defineConfig } from vite import vue from vitejs/plugin-vue// https://vite.dev/config/ export default defineConfig({plugins: [vue()], })修改vite.config.ts后文件内容 新增server内容 server:{host…...
Netty面试内容整理-Netty 概述
Netty 是一个基于 Java 的异步事件驱动网络应用框架,常用于构建高性能、高并发的网络服务。Netty 封装了 Java NIO 的复杂细节,使得开发者可以方便地构建高效的网络应用。以下是 Netty 的概述: Netty 的特点 ● 异步和事件驱动:Netty 采用异步非阻塞的 I/O 模型,基于事件驱…...
Docker搭建达梦数据库--基于 X86 架构
1、部署环境 X86_64 架构的服务器 1 台,安装好docker 拉取镜像 官方最新非授权版本有些函数方法无法使用 docker pull registry.cn-hangzhou.aliyuncs.com/qiluo-images/dm8_single:dm8_20230808_rev197096_x86_rh6_64查看镜像 docker images | grep dm8运行容器 docker run…...
4. 设计模式分类
4.1 创建型模式 这类模式提供创建对象的机制,能够提升已有代码的灵活性和可复用性。 序 号 类 型 业务场景 实现要点 1 工 厂 方 法 多种类型商品不同接口,统一发奖服 务搭建场景 定义一个创建对象的接口,让其子类自 己决定实例化哪一个工厂类,工厂模式 使其创建过程延迟…...