当前位置: 首页 > news >正文

Linux-多线程

目录

  • 线程概念
  • 线程控制
    • 创建
    • 退出
    • 等待
      • join实例
      • detach实例
    • 实例
  • 线程安全
    • 概念
    • 互斥
    • 同步
    • 生产者与消费者模型
      • 实例
    • 信号量
  • 线程应用

线程概念

线程概念
有一个零件加工工厂,工厂中有一个或多个工人
工人是干活的,工厂是集体设备资源的载体
进程就是工厂,线程就是工人。
进程是系统进行资源分配的基本单元;线程是系统进行调度的基本单元。
线程就是进程中的一条执行流程。Linux下线程执行流程是通过pcb实现的,而一个进程中可以有多个线程(pcb),并且这些pcb共享了进程中的大部分资源,相较于传统pcb较为轻量化,因此linux下的线程也被称作为轻量级进程。
通俗理解:在最早学习进程的时候,认为进程就是pcb,是一个程序运行的动态描述,通过这个描述符操作系统实现程序运行调度以及管理。
但是在学习到线程的时候,才知linux下的pcb实际上是一个线程,是进程中的一条执行流程,一个进程中可以有多个pcb,这些pcb共享进程中的大部分资源,因此也被成为轻量级进程。

线程间的独有与共享
独有:标识符,栈,寄存器,信号屏蔽字,errno…
共享:虚拟地址空间,IO信息,信号处理方式,工作路径…
多进程与多线程进行多任务处理的优缺点
线程优点
1.线程间通信更加灵活(包括进程间通信方式在内还可以使用全变量或者函数传参来实现)
2.线程的创建与销毁成本更低。
3.线程间的切换调度成本更低。
进程优点
稳定,健壮性高。—主程序安全性要求高的场景–shell、服务器
多任务处理中,并不是进程或者线程越多越好。执行流太多反而会增加切换调度的成本。
IO密集型程序:程序中大量进行IO操作
cpu密集型程序:程序汇总大量进行数据运算操作

线程控制

线程控制:创建,退出,等待,分离
linux并没有向上提供用于创建线程的接口,因此大牛们对系统调用接口进行封装实现了上层用户态的线程控制接口

创建

int pthread_create(pthread_t *tid, pthread_attr_t *attr, void*(*thread_routine)(void*), void *arg)

参数说明
pthread_t *tid:用于获取线程id,tid是线程的操作句柄,实际上是线程在虚拟地址空间中自己所相对独有的一块空间的首地址
pthread_attr_t *attr:用于线程属性设置
void*(*)(void*):线程入口函数
void *arg:传递给线程的数据
返回值:成功返回0;失败返回非0值

pthread_self()
返回线程id

退出

退出:如何退出线程
线程入口函数中return(线程入口函数运行完毕,则线程会退出)
主线程main函数中return退出的是进程

void pthread_exit(void *retval);

void pthread_exit(void *retval); 在任意位置调用都可以退出线程
主线程退出,并不会导致进程退出;所有线程退出,才会退出进程
进程退出,会退出所有的线程

int pthread_cancel(pthread_t thread);

int pthread_cancel(pthread_t tid);
取消指定的线程(被动退出)

等待

等待:等待一个指定的线程退出,获取退出线程的返回值,回收线程资源。
线程推出后,默认也不会自动释放资源,需要被等待。
处于joinable状态的线程退出后不会自动释放资源

//阻塞接口
int pthread_join(pthread_t tid, void **retval)

tid:指定要等待的线程id;
retval:用于获取退出线程返回值
返回值:成功返回0;失败返回非0-错误编号;

join实例

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<pthread.h>void *thr_entry(void *arg)
{char *ptr = "nice day\n";sleep(3);return (void*)ptr;
}
int main(int argc, char *argv[])
{pthread_t tid;int ret = pthread_create(&tid, NULL, thr_entry, NULL);if(ret != 0){ printf("thread create error\n");return -1;}void *retval = NULL;pthread_join(tid, &retval);printf("retval:%s\n", retval);while(1){printf("i am main thread\n");sleep(1);}return 0;
}

线程有个属性-分离属性,这个属性默认是joinable状态,处于joinable的线程推出之后不会自动释放资源,需要被其他线程等待。
分离:将线程的分离属性,设置为detach状态
处于detach状态的线程退出后,会自动释放资源,不需要被等待。
应用场景:不关心线程的退出返回值,也不想等待一个线程退出

int pthread_detach(pthread_t tid);

detach实例

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<pthread.h>void *thr_entry(void *arg)
{char *ptr = "nice day\n";sleep(3);return (void*)ptr;
}
int main(int argc, char *argv[])
{pthread_t tid;int ret = pthread_create(&tid, NULL, thr_entry, NULL);if(ret != 0){ printf("thread create error\n");return -1;}pthread_detach(tid);//void *retval = NULL;//pthread_join(tid, &retval);//printf("retval:%s\n", retval);while(1){printf("i am main thread\n");sleep(1);}return 0;
}
## 查看
```powershell
ps -ef -L | grep create

ps -ef | grep create:查看create程序的进程信息
-L:查看轻量级进程信息
在每个线程的pcb中都有一个pid和tgid
pid是轻量级进程id-LWP;tgid是线程组id-默认等于主线程的pid
tid:这是一个地址–线程独有空间的地址
每个线程被创建,都会在进程的虚拟地址空间中开辟出一块相对独立的空间

实例

//create.c
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<pthread.h>void *thr_entry(void *arg)
{printf("%s\n", (char*)arg);while(1){printf("i am normal thread--%p-%d\n", pthread_self(), getpid());sleep(5);pthread_exit(NULL);}return NULL;
}
int main(int argc, char *argv[])
{pthread_t tid;int ret;char *ptr = "nice day\n";ret = pthread_create(&tid, NULL, thr_entry, (void*)ptr);if(ret != 0){printf("pthread_create failed!\n");return -1;}//sleep(3);//pthread_cancel(tid);while(1){//打印线程id和进程idprintf("i am main thread--%p-%d\n", pthread_self(), getpid());sleep(1);}return 0;
}

线程安全

概念

概念:描述的是线程中对临界资源的访问操作是安全的
实现:同步与互斥
同步通过条件判断使对临界资源访问或获取更加合理
互斥通过对临界资源同一时间的唯一访问保证访问操作安全

互斥

互斥的实现:互斥锁
互斥锁:本质就是一个只有0/1的计数器,用于标记临界资源的访问状态;0-不可访问;1-可访问。
实现互斥原理,在访问临界资源之前加锁:通过计数器判断是否可访问,不可访问则阻塞;访问资源完毕之后解锁:通过计数器将资源标记为可访问,唤醒阻塞。
互斥锁自身计数的操作是一个原子操作
接口介绍:
1.定义互斥锁变量
pthread_mutex_t mutex;
2.初始化互斥锁变量
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
3.在访问临界资源之前加锁
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);加锁失败则立即返回错误编号-EBUSY
4.在访问临界资源之后解锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);
5.销毁互斥锁
int pthread_mutex_destroy(pthread_mutex_t *mutex);

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<pthread.h>int tickets = 100;void *scalpers(void *arg)
{pthread_mutex_t *mutex = (pthread_mutex_t*)arg;while(1){pthread_mutex_lock(mutex);if(tickets > 0){usleep(1);printf("I got a ticket: %d\n", tickets);tickets--;}else{pthread_exit(NULL);pthread_mutex_unlock(mutex);}pthread_mutex_unlock(mutex);}return NULL;
}
int main(int argc, char *argv[])
{pthread_mutex_t mutex;pthread_t tid[4];int ret;pthread_mutex_init(&mutex, NULL);for(int i = 0; i < 4; i++){ret = pthread_create(&tid[i], NULL, scalpers, &mutex);if(ret != 0){printf("thread create error\n");return -1;}}for(int i = 0; i < 4; i++){pthread_join(tid[i], NULL);}pthread_mutex_destroy(&mutex);return 0;
}

死锁:程序运行流程因为某种原因卡死无法继续推进
死锁产生的原因:死锁产生的四个必要条件
1.互斥条件:一个资源同一事件只有一个进程/线程能够访问
2.不可剥夺条件:我加的锁只有我能解,别人不能解
3.请求与保持条件:加了A锁后请求B锁,B请求不到,A不释放
4.环路等待条件:线程1加了A锁,请求B锁;线程2加了B请求A
预防死锁:破坏死锁产生的必要条件(3.4)
1.一定保证加/解锁顺序一致
2.请求不到第二个锁则释放已有的
避免死锁:银行家算法
已有资源—线程已有资源—线程请求新的资源

同步

同步的实现:通过条件判断实现对资源获取的合理性–条件变量
条件变量:pcb等待队列+能够使线程阻塞以及唤醒线程阻塞的接口
条件变量使用中,对条件判断由程序员自己完成,而条件判断的依据使一个临界资源,访问时需要被保护,因此条件变量需要搭配互斥锁一起使用。

接口介绍
1.定义条件变量
pthread_cond_t cond;
2.初始化条件变量
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *attr);
3.使线程阻塞
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, struct timespec *abstime)
4.唤醒阻塞的线程:
int pthread_cond_signal(pthread_cond_t *cond);-至少唤醒一个
int pthread_cond_broadcast(pthread_cond_t *cond);-唤醒所有
5.销毁条件变量
int pthread_cond_destroy(pthread_cond_t *cond);

注意:条件变量需要搭配互斥锁一起使用;
因此资源是其他线程促使条件满足,其中的条件是否满足也是临界资源的判断。因此需要加锁保护。

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<pthread.h>pthread_mutex_t mutex;
pthread_cond_t cond_customer;
pthread_cond_t cond_cooker;
int bowl = 1;
void *cooker(void *arg)
{while(1){//lockpthread_mutex_lock(&mutex);while(bowl == 1){//waitpthread_cond_wait(&cond_cooker, &mutex);}//cookprintf("cook finish\n");bowl++;//wake customerpthread_cond_signal(&cond_customer);//unlockpthread_mutex_unlock(&mutex);}return NULL;
}
void *customer(void *arg)
{//lock  pthread_mutex_lock(&mutex);while(1){while(bowl == 0){//waitpthread_cond_wait(&cond_customer, &mutex);}//eatprintf("nice\n");bowl--;//wake cookerpthread_cond_signal(&cond_cooker);//unlockpthread_mutex_unlock(&mutex);}return NULL;
}
int main(int argc, char *argv[])
{pthread_t ctid, dtid;int ret;pthread_mutex_init(&mutex, NULL);pthread_cond_init(&cond_customer, NULL);pthread_cond_init(&cond_cooker, NULL);for(int i = 0; i < 4; i++){ret = pthread_create(&ctid, NULL, cooker, NULL);if(ret != 0){printf("thread create error\n");return -1;}}  for(int i = 0; i < 4; i++){ret = pthread_create(&dtid, NULL, customer, NULL);if(ret != 0){printf("thread create error\n");return -1;}}pthread_join(ctid, NULL);pthread_join(dtid, NULL);pthread_mutex_destroy(&mutex);pthread_cond_destroy(&cond_customer);pthread_cond_destroy(&cond_cooker);return 0;
}

注意事项
一、条件变量使用过程中,条件的判断应该使用循环操作
1.四个顾客因为没有饭陷入阻塞
2.厨师做好饭,唤醒了四个顾客
3.四个顾客中,一个加锁成功吃饭,三个卡在加锁这里
4.加锁成功的顾客吃完饭唤醒厨师解锁
5.有可能抢到锁的不是厨师而是顾客
6.如果没有循环判断则会加锁成功后在没有饭时吃饭
二、条件变量使用过程中,若有多种角色则需要使用多个条件变量,不同的角色,分开等待,分开唤醒,防止唤醒角色错误

生产者与消费者模型

生产者与消费者模型:一种非常典型的设计模式。
设计模式:大佬们针对典型的应用场景设计的解决方案
应用场景:有大量数据产生以及进行处理的场景
优势:解耦合,支持忙闲不均,支持并发
实现:两种角色的线程+线程安全的队列(阻塞队列)
线程安全
生产者与生产者:互斥
消费者与消费者:互斥
生产者与消费者:同步+互斥
线程安全的阻塞队列的实现

实例

#include <iostream>
#include <queue>
#include <pthread.h>#define MAX_QUEUE 5
class BlockQueue
{
private:int _capacity;//容量std::queue<int> _queue;pthread_mutex_t _mutex;pthread_cond_t _cond_pro;pthread_cond_t _cond_cus;
public:BlockQueue(int cap = MAX_QUEUE): _capacity(cap){pthread_mutex_init(&_mutex, NULL);pthread_cond_init(&_cond_pro, NULL);pthread_cond_init(&_cond_cus, NULL);}~BlockQueue(){pthread_mutex_destroy(&_mutex);pthread_cond_destroy(&_cond_pro);pthread_cond_destroy(&_cond_cus);}bool Push(int data){pthread_mutex_lock(&_mutex);while(_queue.size() == _capacity){pthread_cond_wait(&_cond_pro, &_mutex);}_queue.push(data);pthread_cond_signal(&_cond_cus);pthread_mutex_unlock(&_mutex);return true;}bool Pop(int *data){pthread_mutex_lock(&_mutex);while(_queue.empty){pthread_cond_wait(&_cond_cus, &_mutex);}*data = _queue.front();_queue.pop();pthread_cond_signal(&_cond_pro);pthread_mutex_unlock(&_mutex);return true;}
};void *productor(void *arg)
{BlockQueue *q = (BlockQueue*)arg;int i = 0;while(1){q->Push(i);printf("%p-push data:%d\n", pthread_self(), i++);}return NULL;
}
void *customer(void *arg)
{BlockQueue *q = (BlockQueue*)arg;while(1){int data;q->Pop(&data);printf("%p-get data:%d\n", pthread_self(), data);}return NULL;
}
int main(int argc, char *argv[])
{BlockQueue q;int count = 4, ret;pthread_t ptid[4], ctid[4];for(int i = 0; i < count; i++){ret = pthread_creat(&ptid[i], NULL, productor, &q);if(ret != 0){	printf("thread create error\0");return 0;}}for(int i = 0; i < count; i++){ret = pthread_creat(&ctid[i], NULL, customer, &q);if(ret != 0){	printf("thread create error\0");return 0;}}for(int i = 0; i < count; i++){pthread_join(ptid[i], NULL);pthread_join(ctid[i], NULL);}return 0;
}

信号量

信号量
本质:计数器
作用:实现进程或线程间的同步与互斥
操作
P操作:计数-1,计数<0则阻塞执行流
V操作:计数+1,唤醒一个阻塞的执行流
同步的实现:通过对计数器对资源进行计数
在获取资源之前,进行P操作;产生资源之后,进行V操作
互斥的实现:初始化值为1,表示资源只有一个
在访问资源之前进行P操作;访问完毕之后进行V操作

接口认识:
1.定义信号量:sem_t sem;
2.初始化信号量
int sem_init(sem_t *sem, int pshared, unsigned int value)
sem:信号量;
pshared:0-线程间/!0-进程间;
value:要设置的初值;
返回值:成功返回0;失败返回-1。
3.P操作:
int sem_wait(sem_t *sem);-阻塞
int sem_trywait(sem_t *sem);-非阻塞
int sem_timedwait(sem_t *sem, struct timespec *timeout);
4.V操作:
int sem_post(sem_t *sem);
5.销毁信号量:
int sem_destroy(sem_t *sem);

线程应用

相关文章:

Linux-多线程

目录 线程概念线程控制创建退出等待join实例detach实例 实例 线程安全概念互斥同步生产者与消费者模型实例 信号量 线程应用 线程概念 线程概念&#xff1a; 有一个零件加工工厂&#xff0c;工厂中有一个或多个工人 工人是干活的&#xff0c;工厂是集体设备资源的载体 进程就是…...

深入解析C++树形关联式容器:map、set及其衍生容器的使用与原理

文章目录 一、引言二、关联式容器的中的 paira.pair 的创建及使用b.pair 间的比较 三、 map 与 set 详解1. map 的基本操作2. set 的基本操作3.关联式容器的迭代器 四、 multimap 与 multiset 的特性五、关联式容器的使用技巧与注意事项1. 键值类型的选择与设计2. 自定义比较函…...

c++基础知识(一)

C字符集&#xff1a;通常将一个标准中能够表示所有字符的一个集合称为字符集。例如Unicode字符集、ASCll、GB2312、BIG5&#xff08;繁体中文及其相关字符&#xff09;等。 字符集是组成程序设计语言的基本要素。由单字符、关键字、标识符、运算符&#xff08;操作符&#xff…...

Midjourney绘图欣赏系列【人物篇】(一)

Midjourney介绍 Midjourney 是生成式人工智能的一个很好的例子&#xff0c;它根据文本提示创建图像。它与 Dall-E 和 Stable Diffusion 一起成为最流行的 AI 艺术创作工具之一。与竞争对手不同&#xff0c;Midjourney 是自筹资金且闭源的&#xff0c;因此确切了解其幕后内容尚不…...

2024 年 2 月 NFT 行业动态:加密货币飙升,NFT 市场调整

作者&#xff1a;stellafootprint.network 数据来源&#xff1a;NFT 研究页面 - Footprint Analytics 2024 年 2 月&#xff0c;加密货币与 NFT 市场显现出了复杂性。该月&#xff0c;NFT 领域的交易量达到 12 亿美元&#xff0c;环比下降了 3.7%。值得关注的是&#xff0c;包…...

【C++那些事儿】深入理解C++类与对象:从概念到实践(下)| 再谈构造函数(初始化列表)| explicit关键字 | static成员 | 友元

&#x1f4f7; 江池俊&#xff1a;个人主页 &#x1f525; 个人专栏&#xff1a;✅C那些事儿 ✅Linux技术宝典 &#x1f305; 此去关山万里&#xff0c;定不负云起之望 文章目录 1. 再谈构造函数1.1 构造函数体赋值1.2 初始化列表1.3 explicit 关键字 2. static成员2.1 概念…...

前端面试 ===> 【Vue2】

Vue2 相关面试题总结 1. 谈谈对Vue的理解 Vue是一种用于构建用户页面的渐进式JavaScript框架&#xff0c;也是一个创建SPA单页面应用的Web应用框架&#xff0c;Vue的核心是 数据驱动试图&#xff0c;通过组件内特定的方法实现视图和模型的交互&#xff1b;特性&#xff1a;&a…...

面试 Java 并发编程八股文十问十答第四期

面试 Java 并发编程八股文十问十答第四期 作者&#xff1a;程序员小白条&#xff0c;个人博客 相信看了本文后&#xff0c;对你的面试是有一定帮助的&#xff01;关注专栏后就能收到持续更新&#xff01; ⭐点赞⭐收藏⭐不迷路&#xff01;⭐ 1&#xff09;Java 中你怎样唤醒…...

物体检测-系列教程27:YOLOV5 源码解析17(训练脚本解读:训练函数4)

&#x1f60e;&#x1f60e;&#x1f60e;物体检测-系列教程 总目录 有任何问题欢迎在下面留言 本篇文章的代码运行界面均在Pycharm中进行 本篇文章配套的代码资源已经上传 点我下载源码 24、epoch循环训练------更新、评估、保存 这部分是训练过程的每个epoch结束之前执行的一…...

基于51单片机的数字时钟(万年历)设计与实现

基于51单片机的数字时钟&#xff08;万年历&#xff09;设计与实现 摘要 随着科技的不断发展&#xff0c;数字时钟已成为人们日常生活中不可或缺的一部分。基于51单片机的数字时钟&#xff08;万年历&#xff09;设计&#xff0c;结合了传统时钟的功能与现代电子技术&#xf…...

2024年谷歌SEO的趋势预测及应对建议(川圣SEO)蜘蛛池

baidu搜索&#xff1a;如何联系八爪鱼SEO&#xff1f; baidu搜索&#xff1a;如何联系八爪鱼SEO&#xff1f; baidu搜索&#xff1a;如何联系八爪鱼SEO&#xff1f; 虽然说“SEO”已死这个口号已经喊了很多年了&#xff08;最终也没死&#xff09;&#xff0c;但是在2023年很…...

Rust 生命周期符号使用的方法和规律

一、生命周期符号使用的规律 在 Rust 中&#xff0c;生命周期&#xff08;lifetimes&#xff09;是用于处理引用和所有权问题的关键概念。生命周期符号&#xff08;通常表示为 a、b 等&#xff09;用于指定引用的有效时间范围。这有助于编译器确保在引用被使用时&#xff0c;所…...

生成哈夫曼树(100%用例)C卷(JavaPythonC++Node.jsC语言)

给定长度为n的无序的数字数组,每个数字代表二叉树的叶子节点的权值,数字数组的值均大于等于1。请完成一个函数,根据输入的数字数组,生成哈夫曼树,并将哈夫曼树按照中序遍历输出。 为了保证输出的二又树中序遍历结果统一,增加以下限制:二叉树节点中,左节点权值小于等于右…...

el-form-item内的el-select如何自适应宽度

最近在使用element-ui做后台管理的时候&#xff0c;有个需求是在弹窗组件里面&#xff0c;添加一个el-select下拉框选项&#xff0c;但是给el-select设置的宽度无法自适应&#xff0c;原因很简单&#xff0c;我们不需要设置固定宽度&#xff0c;设置百分比就行了&#xff0c;让…...

什么洗地机值得推荐?旗舰洗地机希亦、追觅、西屋、海尔实际表现如何?

洗地机这个产品相信大家已经不陌生了&#xff0c;它集合吸尘器和电动扫地拖把的功能&#xff0c;轻轻推拉便可以解决地面上的赃物&#xff0c;且不用我们手动清洗滚刷&#xff0c;深得家务人的喜爱&#xff0c;可是&#xff0c;当我们真正要去选购的时候&#xff0c;还是很纠结…...

掘根宝典之C++隐式类型转化(整型提升,算术转换)

赋值中的隐式转换 话不多说&#xff0c;我们直接看例子 bool b42; //b为真 int ib; //i的值为1 i3.14; //i的值为3 double pi i; // pi的值为3.0 unsigned char c -1; // 假设char占8比特&#xff0c;c的值为255 s…...

group by order by having where union

力扣题目链接 having where 区别 having子句用于分组后筛选&#xff0c;where子句用于行条件筛选 having一般都是配合group by 和聚合函数一起出现如(count(),sum(),avg(),max(),min()) where条件子句中不能使用聚集函数&#xff0c;而having子句就可以。 having只能用在group…...

微信私信短剧机器人源码

本源码仅提供参考&#xff0c;有能力的继续开发 接口为api调用 云端同步 https://ys.110t.cn/api/ajax.php?actyingshilist 影视搜索 https://ys.110t.cn/api/ajax.php?actsearch&name剧名 每日更新 https://ys.110t.cn/api/ajax.php?actDaily 反馈接口 https://ys.11…...

java使用ws.schild.jave将视频转成mp4

<?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation"http://maven.apache.org/POM/4.0.0 http://…...

python map函数

python map函数 文章目录 python map函数 在Python中&#xff0c; map()函数用于将一个函数应用于可迭代对象&#xff08;如列表或元组&#xff09;中的每个元素&#xff0c;并返回一个包含结果的新的可迭代对象。 map()函数的语法如下&#xff1a; map(function, iterable)其…...

地震勘探——干扰波识别、井中地震时距曲线特点

目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波&#xff1a;可以用来解决所提出的地质任务的波&#xff1b;干扰波&#xff1a;所有妨碍辨认、追踪有效波的其他波。 地震勘探中&#xff0c;有效波和干扰波是相对的。例如&#xff0c;在反射波…...

DAY 47

三、通道注意力 3.1 通道注意力的定义 # 新增&#xff1a;通道注意力模块&#xff08;SE模块&#xff09; class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...

【机器视觉】单目测距——运动结构恢复

ps&#xff1a;图是随便找的&#xff0c;为了凑个封面 前言 在前面对光流法进行进一步改进&#xff0c;希望将2D光流推广至3D场景流时&#xff0c;发现2D转3D过程中存在尺度歧义问题&#xff0c;需要补全摄像头拍摄图像中缺失的深度信息&#xff0c;否则解空间不收敛&#xf…...

页面渲染流程与性能优化

页面渲染流程与性能优化详解&#xff08;完整版&#xff09; 一、现代浏览器渲染流程&#xff08;详细说明&#xff09; 1. 构建DOM树 浏览器接收到HTML文档后&#xff0c;会逐步解析并构建DOM&#xff08;Document Object Model&#xff09;树。具体过程如下&#xff1a; (…...

【Go】3、Go语言进阶与依赖管理

前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课&#xff0c;做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程&#xff0c;它的核心机制是 Goroutine 协程、Channel 通道&#xff0c;并基于CSP&#xff08;Communicating Sequential Processes&#xff0…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)

宇树机器人多姿态起立控制强化学习框架论文解析 论文解读&#xff1a;交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架&#xff08;一&#xff09; 论文解读&#xff1a;交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...

用docker来安装部署freeswitch记录

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

爬虫基础学习day2

# 爬虫设计领域 工商&#xff1a;企查查、天眼查短视频&#xff1a;抖音、快手、西瓜 ---> 飞瓜电商&#xff1a;京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空&#xff1a;抓取所有航空公司价格 ---> 去哪儿自媒体&#xff1a;采集自媒体数据进…...

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中&#xff0c;新增了一个本地验证码接口 /code&#xff0c;使用函数式路由&#xff08;RouterFunction&#xff09;和 Hutool 的 Circle…...

MySQL账号权限管理指南:安全创建账户与精细授权技巧

在MySQL数据库管理中&#xff0c;合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号&#xff1f; 最小权限原则&#xf…...