【Linux】线程Thread
🔥博客主页: 我要成为C++领域大神
🎥系列专栏:【C++核心编程】 【计算机网络】 【Linux编程】 【操作系统】
❤️感谢大家点赞👍收藏⭐评论✍️本博客致力于知识分享,与更多的人进行学习交流
线程概述
线程是进程中的一个执行单元
经典的并发模型,多进程模型,占用大量的内存开销,庞大的进程间调度开销,提供一种开销更小,更轻量级的并发解决方案,多线程技术。
线程在进程中,与进程共享资源和数据,进程是容器,线程是执行单元,进程退出可能导致所有线程退出。
每个进程中都有一个主控线程,还有其他的普通线程,操作系统会给进程分配需要的内存单元。
不支持线程技术的操作系统可以忽略线程概念,进程是调度单位。
多线程操作系统下,进程是内存管理单位,线程才是唯一的调度单元。
线程的CPU分配
Linux操作系统下,线程就是轻量级进程,每个进程分配一个LWP
在Linux操作系统下,所有的调度单位都是进程,淡化线程概念
线程的实现方式
用户线程(User Thread):在用户空间实现的线程,不是由内核管理的线程,是由用户态的线程库来完成线程的管理。可以利用第三方库的形式在不支持线程技术的系统下安装和使用线程,用户级线程的调度开销更小,因为大部分的操作都在用户层完成,无需内核干预
内核线程(Kernel Thread):操作系统中每创建一个线程,系统都会为其创建一个内核对象,此线程系统可以识别支持,会为线程执行资源(时间片)。Control控制器会为每个内核级线程创建内核对象,每个内核级线程都会由CPU进行时间片切换
轻量级进程(LightWeight Process):在内核中来支持用户线程;
进程的蜕化
进程内存资源独占,不与其他人共享,进程是独立的调度单位(无需考虑线程问题)
进程中出现了多个执行单元,讨论考虑线程问题
讨论和分析的蜕化,讨论线程,进程的讨论变为主线程和普通线程的分析和讨论
线程间的共享资源和非共享资源
PCB:共享资源
栈:线程栈空间非共享,每个线程创建后,会分配8MB线程栈
库:库资源共享
堆:共享堆空间
全局资源和静态资源:全局资源共享
代码段:代码段共享
文件描述符表:文件描述符表共享
信号的处理行为(某个线程改变信号行为)对所有线程共享。信号屏蔽字非共享,普通线程拥有独立的屏蔽字,拷贝继承于主线程
TCB:每个线程拥有自己的线程控制块,独立的tid
线程开发相关API接口
NPTL线程库
NPTL线程库是典型的内核级线程库,创建的线程可以被系统标识分配cpu资源(轻量级进程)
native Posix thread library
相关命令
ps aux
进程查看
ps ajx
进程关系查看
ps -eLf
所有线程查看 PID相同的是一组线程
LWP
与 PID
相同的情况表示该进程只有一个线程。
ps -Lf pid
查看某一个进程下的线程
每个线程都会被系统分配lwp 调度编号, 便于系统管理调度线程,但是线程拥有独立的tid,线程id
头文件和函数
#include <pthread.h>
使用线程函数的头文件
pthread_t tid
线程tid类型
pthread_create
创建线程并指定类型pthread_create(pthread_t *tid,NULL,void * (thread_job)(void *),void *arg)
第一个参数是线程id,第二个是线程属性,第三个参数是线程工作地址,第四个参数是线程工作参数。
返回值是err
下面是使用pthread_create
创建线程的简单demo程序,通过PID
和LWP
相同可以看出,这个线程就是主控线程
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <pthread.h>void * thread_job(void *arg)//普通线程的创建
{printf("普通线程被创建,参数为%d\n",*(int*)arg);return NULL;
}int main()
{pthread_t tid;int code=1024;//普通线程都是由主控线程创建的pthread_create(&tid,NULL,thread_job,(void*)&code);printf("普通线程被主控线程创建出来\n");while(1) sleep(1);return 0;
}
在编译时需要链接库
gcc Pthrad_Create.c -lpthread -o app
关于线程函数的错误处理
线程函数出错,会返回错误号(err>0),使用char *errstr=strerror(err)
进行错误判断
下面是打印错误日志的demo程序:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <pthread.h>void * thread_job(void *arg)//普通线程的创建
{//printf("普通线程被创建,参数为%d\n",*(int*)arg);while(1) sleep(1);return NULL;
}int main()
{pthread_t tid;int code=1024;int flags=0;int err;//普通线程都是由主控线程创建的while(1){if((err=pthread_create(&tid,NULL,thread_job,(void*)&code))>0){printf("thread create failed:%s\n",strerror(err));exit(0);//创建失败,进程退出}else{printf("线程%d成功创建\n",++flags);}}return 0;
}
32位Ubuntu操作系统下,一个进程只能创建381个线程
pthread_self
pthread_tid =pthread_self(void)
成功返回当前线程的id
主线程通过pthread_create创建普通线程,成功传出tid与普通线程自身利用pthread_self()得到的tid值相等但是进程状态不相等,因为pthread_self()获取tid时可以保证当前的有效性,主线程在获取tid的时候,普通线程可能已经退出。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <pthread.h>void * thread_job(void *arg)//普通线程的创建
{printf("普通线程tid %x 创建成功\n",(unsigned int)pthread_self());return NULL;
}int main()
{pthread_t tid;int code=1024;int flags=0;int err;//普通线程都是由主控线程创建的if((err=pthread_create(&tid,NULL,thread_job,(void*)&code))>0){printf("thread create failed:%s\n",strerror(err));exit(0);//创建失败,进程退出}printf("Monster Thread Tid:%x 普通线程Tid:%lx \n",(unsigned int)pthread_self(),tid);while(1) sleep(1);//主控线程还在运行,普通线程已经退出return 0;
}
pthread_join(pthread_t tid,void **retval)
线程回收函数,可以回收线程资源的同时获取线程的返回值。经典的阻塞回收函数,会一致等待普通线程结束后,进行回收。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <pthread.h>void * thread_job(void *arg)//普通线程的创建
{printf("普通线程tid %x 创建成功\n",(unsigned int)pthread_self());return (void*)126;
}int main()
{pthread_t tid;int code=1024;int flags=0;int err;void *revtal=NULL;//普通线程都是由主控线程创建的if((err=pthread_create(&tid,NULL,thread_job,(void*)&code))>0){printf("thread create failed:%s\n",strerror(err));exit(0);//创建失败,进程退出}printf("Monster Thread Tid:%x 普通线程Tid:%lx \n",(unsigned int)pthread_self(),tid);pthread_join(tid,&revtal);printf("Thread Exit Code:%ld\n",(long int)revtal);return 0;
}
可以看到,如果线程工作函数可以正常退出,退出码就是线程工作函数的返回值
pthread_cancel(pthread_t tid)
指定线程tid,取消结束线程
下面是简单的取消一个线程的demo程序:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <pthread.h>void * thread_job(void *arg)//普通线程的创建
{//printf("普通线程tid %x 创建成功\n",(unsigned int)pthread_self());while(1){printf("进程正在工作。。。\n");sleep(1);}return (void*)129;
}int main()
{pthread_t tid;int code=1024;int flags=0;int err;void *revtal=NULL;//普通线程都是由主控线程创建的if((err=pthread_create(&tid,NULL,thread_job,(void*)&code))>0){printf("thread create failed:%s\n",strerror(err));exit(0);//创建失败,进程退出}printf("Monster Thread Tid:%x 普通线程Tid:%lx \n",(unsigned int)pthread_self(),tid);sleep(5);pthread_cancel(tid);pause();return 0;
}
线程被成功结束,这个进程下只有一个主控线程在工作
当我们对代码稍作修改:
删除线程工作函数中的调用系统函数:
void * thread_job(void *arg)//普通线程的创建
{//printf("普通线程tid %x 创建成功\n",(unsigned int)pthread_self());while(1){}return (void*)129;
}int main()
{pthread_t tid;int code=1024;int flags=0;int err;void *revtal=NULL;//普通线程都是由主控线程创建的if((err=pthread_create(&tid,NULL,thread_job,(void*)&code))>0){printf("thread create failed:%s\n",strerror(err));exit(0);//创建失败,进程退出}printf("Monster Thread Tid:%x 普通线程Tid:%lx \n",(unsigned int)pthread_self(),tid);sleep(5);pthread_cancel(tid);pause(); return 0;
}
结果显示,pthread_cancel
并没有成功取消线程。
这有些类似于信号,信号处理的三个切换条件:系统调用,软件中断,软件异常。而pthread_cancel
取消事件的处理条件,必须有系统调用。
有一个专门为pthread_cancel
提供系统调用的空函数:void pthread_testcancel(void)
,提供一次空的调用。
在线程工作函数加入这个函数再次尝试:
void * thread_job(void *arg)//普通线程的创建
{//printf("普通线程tid %x 创建成功\n",(unsigned int)pthread_self());while(1){pthread_testcancel(void);}return (void*)129;
}int main()
{pthread_t tid;int code=1024;int flags=0;int err;void *revtal=NULL;//普通线程都是由主控线程创建的if((err=pthread_create(&tid,NULL,thread_job,(void*)&code))>0){printf("thread create failed:%s\n",strerror(err));exit(0);//创建失败,进程退出}printf("Monster Thread Tid:%x 普通线程Tid:%lx \n",(unsigned int)pthread_self(),tid);sleep(5);pthread_cancel(tid);pause(); return 0;
}
可以看到只要一个主控线程,普通线程被结束了。
当我们对线程进行取消后,回收一下资源打印退出码看一下:
void * thread_job(void *arg)//普通线程的创建
{//printf("普通线程tid %x 创建成功\n",(unsigned int)pthread_self());while(1){pthread_testcancel(void);}return (void*)129;
}int main()
{pthread_t tid;int code=1024;int flags=0;int err;void *revtal=NULL;//普通线程都是由主控线程创建的if((err=pthread_create(&tid,NULL,thread_job,(void*)&code))>0){printf("thread create failed:%s\n",strerror(err));exit(0);//创建失败,进程退出}printf("Monster Thread Tid:%x 普通线程Tid:%lx \n",(unsigned int)pthread_self(),tid);sleep(5);pthread_cancel(tid);pthread_join(tid,&revtal);printf("Thread Exit Code:%ld\n",(long int)revtal);pause(); return 0;
}
可以看到退出码是-1,所以线程指定退出码时不允许使用-1,保留给pthread_cancel
。
pthread_detach(pthread_t tid)
用于将一个线程设置为分离态线程。
可以通过线程属性,批量创建分离态线程,这些线程诞生即是分离态。
线程有回收态和分离态,这两种状态是互斥的。
若修改线程退出状态,从回收态改为分离态,此操作不可逆,不能将分离线程变为回收态
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <pthread.h>void * thread_job(void *arg)//普通线程的创建
{pthread_detach(pthread_self());while(1){pthread_testcancel();}return (void*)129;
}int main()
{pthread_t tid;int code=1024;int flags=0;int err;void *revtal=NULL;//普通线程都是由主控线程创建的if((err=pthread_create(&tid,NULL,thread_job,(void*)&code))>0){printf("thread create failed:%s\n",strerror(err));exit(0);//创建失败,进程退出}printf("Monster Thread Tid:%x 普通线程Tid:%lx \n",(unsigned int)pthread_self(),tid);if((err=pthread_join(tid,&revtal))>0){printf("join call failed %s\n",strerror(err));}printf("Thread Exit Code:%ld\n",(long int)revtal);pause();return 0;
}
join
和detach
会竞争时间片,所以有时线程以detach
分离态被创建,当线程处于分离态时,无法手动回收,join
执行失败
如果对一个分离态线程进行回收操作,pthread_join
会失效返回。对一个已经处于回收阶段(join已经开始阻塞等待了)的线程设置分离,分离设置失败。
一个自行回收,一个由系统回收,一个可以得到线程退出码,一个无法获取
pthread_exit(void * exitcode)
线程退出函数,结束当前线程,与进程无关,退出码可以被join
获取
线程的退出方式
return 0
主线程结束进程;结束普通线程,返回结果
pthread_cancel
主线程结束,与进程无关
普通进程结束,与进程无关
使用普通线程对主线程进行回收,产生了僵尸级线程
pthread_exit()
主线程结束,与进程无关
普通进程结束,与进程无关
exit()
进程退出,释放所有线程
线程属性
在使用创建线程函数时指定线程类型pthread_create(pthread_t *tid,pthread_attr_t *attr,void * (thread_job)(void *),void *arg)
用第二个参数指定线程属性
pthread_attr_t
线程属性类型,是一个结构体类型
修改线程程属性的操作
使用pthread_attr_getdetachstate
查看线程默认退出属性
使用pthread_attr_getstack
查看栈属性
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <signal.h>
#include <pthread.h>int main()
{int State;void *stack_addr;//栈空间地址size_t stacksize;//栈空间默认大小pthread_attr_t attr;pthread_attr_init(&attr);pthread_attr_getdetachstate(&attr,&State);if(State==PTHREAD_CREATE_JOINABLE)printf("Thread Default State is Join\n");elseprintf("Thread Default State is Detach\n");pthread_attr_getstack(&attr,&stack_addr,&stacksize);printf("线程栈地址为%p 栈大小为%d\n",stack_addr,stacksize);return 0;
}
使用pthread_attr_setdetachstate
修改线程默认退出状态为分离态
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <signal.h>
#include <pthread.h>void *thread_job(void * arg)
{}int main()
{int State;void *stack_addr;//栈空间地址size_t stacksize;//栈空间默认大小pthread_attr_t attr;pthread_attr_init(&attr);pthread_attr_getdetachstate(&attr,&State);if(State==PTHREAD_CREATE_JOINABLE)printf("Thread Default State is Join\n");elseprintf("Thread Default State is Detach\n");pthread_attr_getstack(&attr,&stack_addr,&stacksize);printf("线程栈地址为%p 栈大小为%d\n",stack_addr,stacksize);pthread_t tid;pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);pthread_create(&tid,&attr,thread_job,NULL);int err=pthread_join(tid,NULL);if(err>0)printf("Join Call Failed:%s\n",strerror(err));return 0;
}
使用pthread_attr_setstacksize
修改默认线程栈大小
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <signal.h>
#include <pthread.h>void *thread_job(void * arg)
{}int main()
{int State;void *stack_addr;//栈空间地址size_t stacksize;//栈空间默认大小pthread_attr_t attr;pthread_attr_init(&attr);pthread_attr_getdetachstate(&attr,&State);if(State==PTHREAD_CREATE_JOINABLE)printf("Thread Default State is Join\n");elseprintf("Thread Default State is Detach\n");pthread_attr_getstack(&attr,&stack_addr,&stacksize);printf("线程栈地址为%p 栈大小为%d\n",stack_addr,stacksize);pthread_t tid;pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);stacksize=0x100000;int err;int flags=0;while(1){if((stack_addr=(void*)malloc(stacksize))==NULL){printf("malloc Stack Failed\n");}pthread_attr_setstacksize(&attr,stacksize);if((err=pthread_create(&tid,&attr,thread_job,NULL))>0){printf("Create failed:%s\n",strerror(err));exit(0);}printf("Thread %d 创建成功\n",++flags);}return 0;
}
由结果可知,当将默认大小变小后,32位系统下可以创建的线程数量增加了。
相关文章:

【Linux】线程Thread
🔥博客主页: 我要成为C领域大神🎥系列专栏:【C核心编程】 【计算机网络】 【Linux编程】 【操作系统】 ❤️感谢大家点赞👍收藏⭐评论✍️ 本博客致力于知识分享,与更多的人进行学习交流 线程概述 …...
RAG技术:在自然语言处理中的深度融合与创新
在自然语言处理(NLP)领域,随着技术的不断进步,我们见证了各种创新方法的涌现。其中,检索增强生成(Retrieval-Augmented Generation,简称RAG)技术以其独特的优势,逐渐成为…...
什么是std::bind
2024年6月29日,周日下午 std::bind 是一个C11标准库中的函数,它用于将一个函数或函数对象与特定的参数绑定在一起,生成一个新的函数对象。 std::bind通常和std::function一起使用,因为std::function可以作为一个函数容器来接收st…...

C语言的数据结构:树与二叉树(哈夫曼树篇)
前言 上篇讲完了二叉树,二叉树的查找性能要比树好很多,如平衡二叉树保证左右两边节点层级相差不会大于1,其查找的时间复杂度仅为 l o g 2 n log_2n log2n,在两边层级相同时,其查找速度接近于二分查找。1w条数据&am…...
docker 安装syslog
Syslog-ng是一个可靠、多功能的日志管理系统,用于收集日志并将其转发到指定的日志分析工具。 使用Docker CLI方式搭建 步骤 1: 拉取Syslog-ng镜像 首先,需要从Docker Hub拉取Syslog-ng的官方镜像。 docker pull balabit/syslog-ng:latest 步骤 2: 启动…...

什么是无头浏览器?
简而言之,无头浏览器是没有图形用户界面 (GUI) 的 Web 浏览器。GUI 包括用户与之交互的数字元素,例如按钮、图标和窗口。但是,关于无头浏览器,您需要了解的还有很多。 在本文中,您将了解什么是…...

【面试干货】与的区别:位运算符与逻辑运算符的深入探讨
【面试干货】&与&&的区别:位运算符与逻辑运算符的深入探讨 1、&:位运算符2、&&:逻辑运算符3、&与&&的区别 💖The Begin💖点点关注,收藏不迷路💖 & 和 …...

搭建Renesas R7FA8D1BHECBD-BTB的开发调试环境(DAP-LINK: N32G45XVL-STB)
目录 概述 1 软硬件 1.1 软硬件环境信息 1.2 开发板信息 1.3 调试器信息 2 FSP和KEIL产生测试项目 2.1 FSP生成项目 2.2 Keil中配置 3 硬件连接框图 4 一个测试案例 4.1 功能介绍 4.2 定时器函数 5 测试 搭建Renesas R7FA8D1BHECBD-BTB的开发调试环境(…...

探索人工智能和LLM对未来就业的影响
近年来,人工智能(AI)迅猛发展,引发了人们的兴奋,同时也引发了人们对就业未来的担忧。大型语言模型(LLM)就是最新的例子。这些强大的人工智能子集经过大量文本数据的训练,以理解和生成…...
钓鱼网站原理与攻防
知识点:LAMP平台部署,Web架构分析,钓鱼网站原理与搭建 中间件: 中间件是一种独立的软件,位于客户机和服务器之间,主要用于在网络环境中进行数据的传输和通信。它充当客户端和服务端之间的桥梁,…...

Windows 中 Chrome / Edge / Firefox 浏览器书签文件默认存储路径
1. Chrome 浏览器 按组合键 Win R,打开运行对话框,输入 %USERPROFILE%\AppData\Local\Google\Chrome\User Data\Default或在Chrome 浏览器地址栏输入 chrome://version查看【个人资料路径】 2. Edge 浏览器 按组合键 Win R,打开运行对…...

秋招Java后端开发冲刺——关系型数据库篇(Mysql)
本文介绍关系型数据库及其代表Mysql数据库,并介常见面试题目。 一、数据库概述 1. 数据库(Database, DB):是长期储存在计算机内的、有组织的、可共享的数据集合。 2. 数据库管理系统(Database Management System, D…...

DHCP原理1-单个局域网出现多个DHCP服务器会发生什么
1. 背景 DHCP全称是Dynamic Host Configuration Protocol。其协议标准是RFC1541(已被RFC2131取代),主要实现服务器向客户端动态分配IP地址(如IP地址、子网掩码、网关、DNS)和配置信息。其系统架构是标准的C/S架构。RFC…...
24/06/29(21.1205)程序的编译和链接
源文件 ---> 可执行文件,这一过程要执行的流程: 预处理 编译 汇编 链接 组成每一个程序的每个源文件通过编译过程分别转换成目标代码;每个目标代码由链接器捆绑在一起,形成一个单一而完整的可执行程序;链接器同时也会引入标准函数库中任何被该程序所用到的函数,而且它可以…...
使用Java Executors框架处理并发任务
一、并发与Java Executors框架简介 一、并发编程的重要性 并发编程是现代编程中最重要的概念之一。在更多的核心和更快的处理器出现的今天,如何充分利用这些资源就变得异常重要。并发编程允许你的程序同时处理多个任务,从而使程序更有效地利用系统资源,提高执行效率。 提…...
LeetCode:经典题之144、94、145、102题解及延伸|二叉树的遍历|前中后层序遍历|Morris算法
系列目录 88.合并两个有序数组 52.螺旋数组 567.字符串的排列 643.子数组最大平均数 150.逆波兰表达式 61.旋转链表 160.相交链表 83.删除排序链表中的重复元素 389.找不同 1491.去掉最低工资和最高工资后的工资平均值 896.单调序列 206.反转链表 92.反转链表II 141.环形链表 …...

ONLYOFFICE 桌面编辑器 8.1全新发布,更强大的编辑工具
ONLYOFFICE 8.1 一、什么是ONLYOFFICE?二、怎么安装 ONLYOFFICE 8.1三、主要功能介绍四、总结 一、什么是ONLYOFFICE? ONLYOFFICE 是一款功能强大的办公套件,旨在提供全面的文档、表格和演示文稿编辑解决方案。它集成了文字处理、电子表格和演…...
百日筑基第六天-了解一下Dubbo
百日筑基第六天-了解一下Dubbo Dubbo 是一款高性能、轻量级的开源 WEB 和 RPC 框架。 Dubbo 提供了六大核心能力: 面向接口代理的高性能 RPC 调用。智能容错和负载均衡。服务自动注册和发现。高度可扩展能力。运行期流量调度。可视化的服务治理与运维。 简单来说…...

微机原理 复习
第一章导论 1.3 冯诺依曼体系结构 (1)以二进制形式表示指令和数据 (2)程序和数据事先放在存储器中(预存储) (3)由运算器、控制器、输入设备和输出设备五大部件组成 字长、主频…...
5年工作经验面试经验以及面试题分享
第一家面试题 评价 全是八股文 面试题 MySQL索引类型 索引结构 联合索引可以设置索引类型 不同索引性能差异巨大 基础索引有哪些 B Tree索引和Hash索引 Redis基本数据结构 List是原子的吗 原子性和可见性区别是什么 MySQL的存储过程和视图 MySQL性能优化有哪些 MySQL的存储…...
Cursor实现用excel数据填充word模版的方法
cursor主页:https://www.cursor.com/ 任务目标:把excel格式的数据里的单元格,按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例,…...

【kafka】Golang实现分布式Masscan任务调度系统
要求: 输出两个程序,一个命令行程序(命令行参数用flag)和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽,然后将消息推送到kafka里面。 服务端程序: 从kafka消费者接收…...
模型参数、模型存储精度、参数与显存
模型参数量衡量单位 M:百万(Million) B:十亿(Billion) 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的,但是一个参数所表示多少字节不一定,需要看这个参数以什么…...

Day131 | 灵神 | 回溯算法 | 子集型 子集
Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣(LeetCode) 思路: 笔者写过很多次这道题了,不想写题解了,大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...

定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...

【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力
引言: 在人工智能快速发展的浪潮中,快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型(LLM)。该模型代表着该领域的重大突破,通过独特方式融合思考与非思考…...
将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?
Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...
土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等
🔍 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术,可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势,还能有效评价重大生态工程…...

用docker来安装部署freeswitch记录
今天刚才测试一个callcenter的项目,所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...

GC1808高性能24位立体声音频ADC芯片解析
1. 芯片概述 GC1808是一款24位立体声音频模数转换器(ADC),支持8kHz~96kHz采样率,集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器,适用于高保真音频采集场景。 2. 核心特性 高精度:24位分辨率,…...