[Linux]:线程(二)
✨✨ 欢迎大家来到贝蒂大讲堂✨✨
🎈🎈养成好习惯,先赞后看哦~🎈🎈
所属专栏:Linux学习
贝蒂的主页:Betty’s blog
与Windows环境不同,我们在linux环境下需要通过指令进行各操作,以下是常见操作的指令:
1. 线程互斥
1.1 基本概念
- 临界资源: 多线程执行流共享的资源叫做临界资源。
- 临界区: 每个线程内部,访问临界资源的代码,就叫做临界区。
- 互斥: 任何时刻,互斥保证有且只有一个执行流进入临界区,访问临界资源,通常对临界资源起保护作用。
- 原子性: 不会被任何调度机制打断的操作,该操作只有两态,要么完成,要么未完成。
进程之间进行通信需要先创建第三方资源,使得不同的进程能够看到同一份资源。由于这份第三方资源可以由操作系统中的不同模块提供,所以进程间通信的方式有很多种。在进程间通信中,这个第三方资源被称为临界资源,而访问第三方资源的代码则被称为临界区。
与之不同的是,多线程的大部分资源都是共享的。因此,线程之间进行通信并不需要像进程那样费力地去创建第三方资源。
例如,我们在代码中只需要在全局区定义一个count变量,新线程可以每隔一秒对该变量进行加一操作,主线程也可以每隔一秒获取count变量的值并进行打印。
#include <iostream>
#include <unistd.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
using namespace std;
int count = 0;
void *Routine(void *args)
{while (true){count++;sleep(1);}pthread_exit((void *)0);
}
int main()
{pthread_t tid;pthread_create(&tid, nullptr, Routine, nullptr);while (true){cout << "The value of count is " << count << endl;sleep(1);}pthread_join(tid, nullptr);return 0;
}

在当前情境下,我们相当于实现了主线程和新线程之间的通信。其中,全局变量count起着关键作用,它被称为为临界资源,原因在于它被多个执行流所共享。而主线程中的 cout 操作以及新线程中的 count++ 操作,被称作临界区。这是因为这些代码片段对临界资源进行了访问。
但是我们同样观察到打印数据并没有 1,这就是多执行流对临界资源操作常引发的数据不一致问题。
同样我们也可以下面抢票程序的实现,具体演示如果不对临界资源进行限制,可能会出现的危害。
#include <iostream>
#include <unistd.h>
#include <pthread.h>
#include <cstdio>
#include <cstdlib>
using namespace std;
int tickets = 1000;
void *getTickets(void *args)
{string name = "thread ";name += to_string((uint64_t)args);while (true){if (tickets > 0){usleep(1000);cout << "[" << name << "]" << "get a ticket,left: " << --tickets << endl;}else{break;}}cout << name << " is quit!" << endl;pthread_exit((void *)0);
}
int main()
{pthread_t tids[5];for (uint64_t i = 0; i < 5; i++){pthread_create(tids + i, nullptr, getTickets, (void *)i);}for (int i = 0; i < 5; i++){pthread_join(tids[i], nullptr);}return 0;
}

剩余票数出现负数,这明显不符合我们的常识与预期,之所以出现这种情况,本质就是 tickets就是我们的临界资源,--tickets也 并不是原子的,在多执行流同时执行时就可能会发生这种问题。
为什么 --tickets并不是原子的呢?
因为从汇编角度看,我们的
--操作其实是不安全的,他们转成汇编,一般会对应三条汇编指令:从内存中读取数据到CPU中;CPU内进行操作;CPU将结果写回内存。进程在运行的时候,随时可能被切换。

1.2 互斥量
为了解决这个问题我们就引入了互斥,保证一次只有一个执行流访问临界资源,而为了实现互斥,我们就需要保证临界区的原子性,即临界区的资源要么被执行完成,要么不执行,只存在这两态。
要做到这些,本质就是需要一把锁,所以 Linux就引入一个锁,并将其称为互斥量。

1.3 互斥量的接口
1.3.1 初始化互斥量
我们可以使用pthread_mutex_init初始化互斥量,使用方法如下:
- 函数原型:int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
- 参数:
mutex:需要初始化的互斥量。attr:初始化互斥量的属性,一般设置为nullptr即可。
- 返回值:互斥量初始化成功返回0,失败返回错误码。
这种调用函数接口初始化互斥量的方式我们称为动态分配,除此之外,我们也能使用如下的方式进行初始化,我们将其称为静态分配。
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
1.3.2 销毁互斥量
我们可以使用pthread_mutex_destory销毁互斥量,使用方法如下:
- 函数原型:int pthread_mutex_destroy(pthread_mutex_t *mutex);
- 参数:
mutex:需要销毁的互斥量。- 返回值:成功返回 0,失败返回错误码。
其中销毁互斥量,需要注意以下几点:
- 使用
PTHREAD_MUTEX_INITIALIZER静态初始化的互斥量不需要销毁。- 不能销毁一个已经加锁的互斥量。
- 已经销毁的互斥量,要确保后面不会有线程再尝试加锁。
1.3.3 加锁互斥量
加锁本质就是让被加锁区域的代码具有原子性,只能同时被一个线程访问。我们可以使用pthread_mutex_lock对互斥量进行加锁,使用方法如下:
- 函数原型:int pthread_mutex_lock(pthread_mutex_t *mutex);
- 参数:
mutex:需要加锁的互斥量。- 返回值:成功返回 0,失败返回错误码。
如果一个线程在执行过程中,遇见该接口,并且该锁已被其他线程申请,那么该线程此时就会陷入阻塞状态,等待其解锁。
1.3.4 解锁互斥量
在加完锁之后,我们不可能让所有代码只被一个执行流访问,所以我们需要合适的地方解锁,我们可以使用pthread_mutex_unlock对互斥量进行解锁,使用方法如下:
- 函数原型:int pthread_mutex_unlock(pthread_mutex_t *mutex);
- 参数:
mutex:需要解锁的互斥量。- 返回值:成功返回 0,失败返回错误码。
知道了这些概念之后我们就可以对前面的抢票逻辑进行修改:
#include <iostream>
#include <unistd.h>
#include <pthread.h>
#include <cstdio>
#include <cstdlib>
using namespace std;
int tickets = 1000;
pthread_mutex_t mutex;
void *getTickets(void *args)
{string name = "thread ";name += to_string((uint64_t)args);while (true){pthread_mutex_lock(&mutex);if (tickets > 0){usleep(1000);cout << "[" << name << "]" << "get a ticket,left: " << --tickets << endl;pthread_mutex_unlock(&mutex);}else{pthread_mutex_unlock(&mutex);break;}}cout << name << " is quit!" << endl;pthread_exit((void *)0);
}
int main()
{pthread_mutex_init(&mutex, nullptr);pthread_t tids[5];for (uint64_t i = 0; i < 5; i++){pthread_create(tids + i, nullptr, getTickets, (void *)i);}for (int i = 0; i < 5; i++){pthread_join(tids[i], nullptr);}pthread_mutex_destroy(&mutex);return 0;
}

其实在大部分情况下,加锁本身都是有损于性能的事,因为它使多执行流由并行执行变为了串行执行,这几乎是不可避免的。所以我们需要在合适的位置加锁与解锁,尽可能减少锁引入锁带来的性能开销成本。
1.4 互斥量的原理
当我们使用互斥量之后,临界区的代码对于其他线程来说,只有两种状态:加锁与解锁,这就保证了临界区的原子性。而我们要知道锁本身就是能被所有执行流访问的资源,所以锁本身也是一种临界资源,当然也需要保证其原子性,所以锁本身实现就是原子的。
为了实现互斥锁操作,大多数体系结构都提供了 swap 或 exchange 指令,该指令的作用就是把寄存器和内存单元的数据相交换,以下就是实现加锁 lock与解锁 unlock的伪代码:

我们首先可以认为 mutex 的初始值为1,al 是计算机中的一个寄存器,当线程申请锁时,需要执行以下步骤:
- 先将
al寄存器中的值清0。该动作可以被多个线程同时执行,因为每个线程都有自己的一组寄存器(上下文信息),执行该动作本质上是将自己的al寄存器清0。 - 然后交换
al寄存器和mutex中的值。xchgb是体系结构提供的交换指令,该指令可以完成寄存器和内存单元之间数据的交换。

- 最后判断
al寄存器中的值是否大于0。若大于0则申请锁成功,此时就可以进入临界区访问对应的临界资源;否则申请锁失败需要被挂起等待,直到锁被释放后再次竞争申请锁。
我们需要注意的是CPU内的寄存器不是被所有的线程共享的,每个线程都有独自的一组寄存器,所以改变当前线程 al寄存器的值并不会影响其他线程的 al寄存器, 当然内存中的数据因为属于同一个进程,所以各个线程是共享的。

而当线程释放锁时,需要执行以下步骤:
- 将内存中的
mutex置回1,使得下一个申请锁的线程在执行交换指令后能够得到1。 - 唤醒等待
mutex的线程,让它们继续竞争申请锁。
在线程释放锁的过程中,并没有将当前线程的 al 寄存器中的值清0,这不会造成任何影响,因为每次线程在申请锁时都会先将自己 al 寄存器中的值清0,再执行交换指令。
所以我们申请锁的本质就是执行 xchgb这一条汇编指令,因为只有一条,所以只有已执行与未执行两种状态,具有原子性。
2. 线程安全
线程安全是指在多线程环境下,多个线程并发执行同一段代码时,不会出现不可预期的错误结果或数据不一致的情况。
常见线程不安全的情况有:
- 不保护共享变量的函数。
- 函数状态随着被调用,状态发生变化的函数。
- 返回指向静态变量指针的函数。
- 调用线程不安全函数的函数。
而线程安全的情况有:
- 每个线程对全局变量或者静态变量只有读取的权限,而没有写入的权限,一般来说这些线程是安全的。
- 类或者接口对于线程来说都是原子操作。
- 多个线程之间的切换不会导致该接口的执行结果存在二义性。
而可重入函数与线程安全的联系有:
- 函数是可重入的,那就是线程安全的。
- 函数是不可重入的,那就不能由多个线程使用,有可能引发线程安全问题。
- 如果一个函数中有全局变量,那么这个函数既不是线程安全也不是可重入的。
而可重入函数与线程安全的区别有:
- 可重入函数是线程安全函数的一种。
- 线程安全不一定是可重入的,而可重入函数则一定是线程安全的。
- 如果对临界资源的访问加上锁,则这个函数是线程安全的,但如果这个重入函数的锁还未释放则会产生死锁,因此是不可重入的。
3. 死锁
死锁是指在一组进程中的各个进程均占有不会释放的资源,但因互相申请被其他进程所占用不会释放的资源而处于的一种永久等待状态。
比如说如果某一执行流连续申请了两次锁,就会陷入死锁状态。具体情况如下:当该执行流第一次申请锁时,通常会申请成功。然而,第二次申请锁时,由于此锁已经被该执行流自身持有,再次申请会失败,进而导致该执行流被挂起。而此时,这个锁在其自己手上,可它又处于被挂起的状态,根本没有机会去释放锁。这样一来,该执行流将永远无法被唤醒,从而处于死锁状态。
#include <iostream>
#include <unistd.h>
#include <pthread.h>
#include <cstdio>
#include <cstdlib>
using namespace std;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void*Routine(void*args)
{pthread_mutex_lock(&mutex);pthread_mutex_lock(&mutex);return nullptr;
}
int main()
{pthread_t tid;pthread_create(&tid,nullptr,Routine,nullptr);pthread_join(tid,nullptr);return 0;
}

其中形成死锁的必要条件有以下四个:
- 互斥条件: 一个资源每次只能被一个执行流使用。
- 请求与保持条件: 一个执行流因请求资源而阻塞时,对已获得的资源保持不放。
- 不剥夺条件: 一个执行流已获得的资源,在未使用完之前,不能强行剥夺。
- 循环等待条件: 若干执行流之间形成一种头尾相接的循环等待资源的关系。
而为了避免死锁我们一般也可以从这几个角度思考:
- 破坏死锁的四个必要条件。
- 加锁顺序一致。
- 避免锁未释放的场景。
- 资源一次性分配。
除此之外,还有一些避免死锁的算法,常见的比如有死锁检测算法和银行家算法。
4. 线程同步
4.1 饥饿问题
线程饥饿指的是某些线程由于各种原因,一直无法获得足够的 CPU 时间来执行任务,从而处于长期等待或执行时间极少的状态。 产生线程饥饿的原因主要有以下几种:
- 高优先级线程抢占:如果系统中有高优先级的线程持续占用 CPU 资源,那么低优先级的线程就可能长时间得不到执行机会,从而导致饥饿。例如,在实时系统中,高优先级的实时任务可能会一直抢占低优先级的普通任务。
- 线程调度不公平:如果线程调度算法不合理或者存在缺陷,可能导致某些线程被不公平地对待,长期无法获得执行机会。比如某些调度算法可能偏向于某些特定类型的线程或者特定状态的线程。
- 资源竞争:当多个线程竞争有限的资源时,一些线程可能因为一直无法获得所需资源而被阻塞,从而无法执行。例如,多个线程竞争一个互斥锁,而某些线程总是在竞争中失败,就可能陷入饥饿状态。
线程饥饿会导致系统性能下降,部分任务无法及时完成,甚至可能使整个系统陷入停滞或出现不可预测的行为。为了解决线程饥饿问题,我们可以让线程与线程之间形成同步关系。
同步:在保证数据安全的前提下,让线程能够按照某种特定的顺序访问临界资源,从而有效避免饥饿问题。
4.2 条件变量
条件变量是利用线程间共享的全局变量进行同步的一种机制,条件变量是用来描述某种资源是否就绪的一种数据化描述。其一般包含两个步骤:
- 一个线程等待条件变量的条件成立而被挂起。
- 另一个线程使条件成立后唤醒等待的线程。
4.3 条件变量的接口
4.3.1 初始化条件变量
我们可以使用pthread_cond_init初始化互斥量,使用方法如下:
- 函数原型:int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
- 参数:
cond:需要初始化的条件变量。attr:初始化条件变量的属性,一般设置为nullptr即可。
- 返回值:条件变量初始化成功返回0,失败返回错误码。
这种调用函数接口初始化条件变量的方式我们称为动态分配,除此之外,我们也能使用如下的方式进行初始化,我们将其称为静态分配。
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
4.3.2 销毁条件变量
我们可以使用pthread_cond_destory销毁互斥量,使用方法如下:
- 函数原型:int pthread_cond_destroy(pthread_cond_t *cond);
- 参数:
mutex:需要销毁的条件变量。- 返回值:成功返回 0,失败返回错误码。
- 使用
PTHREAD_COND_INITIALIZER静态初始化的条件变量不需要销毁。
4.3.3 等待条件变量
当某个线程满足某个条件时,我们就可以将其至于条件变量下等待。
- 函数原型:int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
- 参数:
cond:需要等待的条件变量。
mutex:当前线程所处临界区对应的互斥锁。
- 返回值:成功返回 0,失败返回错误码。
4.3.4 唤醒等待
在满足某个条件之后,我们就可以使用以下两种即可,将等待队列中的线程唤醒。
- 函数原型:
- int pthread_cond_broadcast(pthread_cond_t *cond);
- int pthread_cond_signal(pthread_cond_t *cond);
- 参数:
cond:需要唤醒的条件变量。- 返回值:成功返回 0,失败返回错误码。
其中 pthread_cond_signal()函数用于唤醒等待队列中的第一个线程。pthread_cond_broadcast()函数用于唤醒等待队列中的全部线程。
比如我们下面创建五个线程,然后将其放入等待队列,最后由主线程进行唤醒。
#include <iostream>
#include <cstdio>
#include <pthread.h>
#include <unistd.h>
using namespace std;
#include <string>
pthread_mutex_t mutex;
pthread_cond_t cond;
void *Routine(void *args)
{pthread_detach(pthread_self());string name = "thread " + to_string((uint64_t)args);while (true){pthread_mutex_lock(&mutex);pthread_cond_wait(&cond, &mutex);cout << name << " running..." << endl;pthread_mutex_unlock(&mutex);}
}
int main()
{pthread_t tids[5];pthread_mutex_init(&mutex, nullptr);pthread_cond_init(&cond, nullptr);for (uint64_t i = 0; i < 5; i++){pthread_create(tids + i, nullptr, Routine, (void *)i);}while (true){sleep(1);pthread_cond_signal(&cond);}pthread_mutex_destroy(&mutex);pthread_cond_destroy(&cond);return 0;
}
在调用<font style="color:rgb(28, 31, 35);">pthread_cond_wait</font>函数时需要传入对应的互斥锁,原因如下:
当线程由于某些条件不满足而需要在特定条件变量下进行等待时,必须释放该互斥锁。这是因为如果不释放互斥锁,其他线程将无法获取该锁以进入临界区修改共享资源,从而无法改变条件使等待线程被唤醒。
当该线程被唤醒后,会接着执行临界区内的代码,这就要求该线程必须立即获得对应的互斥锁。这样设计确保了线程在被唤醒后能够安全地访问临界区,避免了多个线程同时进入临界区而导致的数据不一致和资源竞争问题。
4.4 条件变量使用规范
使用条件变量我们一般遵守以下规范,如果是等待条件变量,函数应该放在互斥量加锁与解锁之间,因为判断条件也是一种临界资源。
pthread_mutex_lock(&mutex);
while (条件为假)
pthread_cond_wait(cond, mutex);
//修改条件
pthread_mutex_unlock(&mutex);
同样唤醒操作也需要类似的操作。
pthread_mutex_lock(&mutex);
//条件为真
pthread_cond_signal(cond);
pthread_mutex_unlock(&mutex);
相关文章:
[Linux]:线程(二)
✨✨ 欢迎大家来到贝蒂大讲堂✨✨ 🎈🎈养成好习惯,先赞后看哦~🎈🎈 所属专栏:Linux学习 贝蒂的主页:Betty’s blog 与Windows环境不同,我们在linux环境下需要通过指令进行各操作&…...
【unity进阶知识3】封装一个事件管理系统
前言 框架的事件系统主要负责高效的方法调用与数据传递,实现各功能之间的解耦,通常在调用某个实例的方法时,必须先获得这个实例的引用或者新实例化一个对象,低耦合度的框架结构希望程序本身不去关注被调用的方法所依托的实例对象…...
服务器使用frp做内网穿透详细教程,请码住
目录 1.内网穿透的定义 2.前提条件 3.frp下载地址 4.配置服务器端的frps.toml文件 5. 配置客户端,即物理服务器或者是电脑本机地址 6.添加服务端启动命令startServerFrp.sh 7.添加客户端启动命令startClientFrp.sh 8. 查看服务端启动日志 9.查看客户端启…...
小程序视频编辑SDK解决方案,轻量化视频制作解决方案
面对小程序、网页、HTML5等多样化平台,如何轻松实现视频编辑的轻量化与高效化,成为了众多开发者和内容创作者共同面临的挑战。正是洞察到这一市场需求,美摄科技推出了其领先的小程序视频编辑SDK解决方案,为创意插上翅膀࿰…...
ERROR [internal] load metadata for docker.io/library/openjdk:8
ERROR: failed to solve: DeadlineExceeded: DeadlineExceeded: DeadlineExceeded: openjdk:8: failed to do request: Head “https://registry-1.docker.io/v2/library/openjdk/manifests/8”: dial tcp 202.160.129.6:443: i/o timeout 在构建docker镜像时从docker.io/libr…...
Wed前端--HTML基础
目录 一、开发工具 二、HTML文档结构 2.1头部head 2.1.1title标记 2.1.2元信息meta标记 具体实例 编辑 一、开发工具 最基础的开发工具是:HBuilder 二、HTML文档结构 HTML文档由头部head和主体body组成 头部head标记中可以定义标题样式,头部信…...
Latex 自定义运算符加限定条件的实现
“\operatorname{mean}\limits_{n \in N}” 的效果 mean n ∈ N \operatorname{mean}\limits_{n \in N} meann∈N “\operatorname*{mean}\limits_{n \in N}” 的效果 mean n ∈ N \operatorname*{mean}\limits_{n \in N} n∈Nmean 参考这篇文章...
大数据实时数仓Hologres(三):存储格式介绍
文章目录 存储格式介绍 一、格式 二、使用建议 三、技术原理 1、列存 2、行存 3、行列共存 四、使用示例 存储格式介绍 一、格式 在Hologres中支持行存、列存和行列共存三种存储格式,不同的存储格式适用于不同的场景。在建表时通过设置orientation属性指…...
关于vue2+uniapp+uview+vuex 私募基金项目小程序总结
1.关于权限不同tabbar处理 uniapp 实现不同用户展示不同的tabbar(底部导航栏)_uniapp tabbar-CSDN博客 但是里面还有两个问题 一个是role应该被本地存储并且初始化 第二个问题是假设我有3个角色 每个角色每个tabbar不一样的,点击tabbar时候会导致错乱 第三个问题…...
多线程(一):线程的基本特点线程安全问题ThreadRunnable
目录 1、线程的引入 2、什么是线程 3、线程的基本特点 4、线程安全问题 5、创建线程 5.1 继承Thread类,重写run 5.1.1 创建Thread类对象 5.1.2 重写run方法 5.1.3 start方法创建线程 5.1.4 抢占式执行 5.2 实现Runnable,重写run【解耦合】★…...
启动hadoop集群出现there is no HDFS_NAMENODE_USER defined.Aborting operation
解决方案 在hadoop-env.sh中添加 export HDFS_DATANODE_USERroot export HDFS_NAMENODE_USERroot export HDFS_SECONDARYNAMENODE_USERroot export YARN_RESOURCEMANAGER_USERroot export YARN_NODEMANAGER_USERroot 再次运行即可。...
Redis实现短信登录解决状态登录刷新的问题
Redis实现短信登录 获取验证码控制层 /*** 发送手机验证码*/PostMapping("/code")public Result sendCode(RequestParam("phone") String phone) {// TODO 发送短信验证码并保存验证码return userService.sendCode(phone);} 获取验证码服务层 Result sendC…...
33. java快速排序
1. 前言 排序算法是数据结构中最基础的算法,快速排序则是面试中最常见的排序算法。无论是校招面试还是社招面试,快速排序算法的出现频率远高于其他算法,而且经常会要求候选人白板手写实现算法。快速排序算法的核心是分治处理,重点是分析时间复杂度。 2. 快速排序算法 面试…...
普通二叉搜索树的模拟实现【C++】
二叉搜素树简单介绍 二叉搜索树又称二叉排序树,是具有以下性质的二叉树: 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值 它的左右子树也分别为二叉搜索树 注意…...
unity 介绍Visual Scripting Scene Variables
Visual Scripting中的场景变量是指在Unity中使用可视化脚本时,能够在不同场景间传递和存储数据的变量。这些变量可以用来跟踪游戏状态、玩家信息或其他动态数据,允许开发者在不编写代码的情况下创建复杂的游戏逻辑。 场景变量的优势包括: 1…...
linux服务器部署filebeat
# 下载filebeat curl -L -O https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-7.17.23-linux-x86_64.tar.gz # 解压 tar xzvf filebeat-7.17.23-linux-x86_64.tar.gz# 所在位置(自定义) /opt/filebeat-7.17.23-linux-x86_64/filebeat.ym…...
个人获取Wiley 、ScienceDirect、SpringerLink三个数据库文献的方法
在同学们的求助文献中经常出现Wiley 、ScienceDirect、SpringerLink这三个数据库文献。本文下面就讲解一下个人如何不用求助他人自己搞定这三个数据库文献下载的方法。 个人下载文献首先要先获取数据库资源,小编平时下载文献是通过科研工具——文献党下载器获取的数…...
Java五子棋
目录 一:案例要求: 二:代码: 三:结果: 一:案例要求: 实现一个控制台下五子棋的程序。用一个二维数组模拟一个15*15路的五子棋棋盘,把每个元素赋值位“┼”可以画出棋…...
【从0开始自动驾驶】用python做一个简单的自动驾驶仿真可视化界面
【从0开始自动驾驶】用python做一个简单的自动驾驶仿真可视化界面 废话几句废话不多说,直接上源码目录结构init.pysimulator.pysimple_simulator_app.pyvehicle_config.json 废话几句 自动驾驶开发离不开仿真软件成品仿真软件种类多https://zhuanlan.zhihu.com/p/3…...
一拖二快充线:单接与双接的多场景应用
在当代社会,随着智能手机等电子设备的普及,充电问题成为了人们关注的焦点。一拖二快充线作为一种创新的充电解决方案,因其便捷性与高效性而受到广泛关注。本文将深入探讨一拖二快充线的定义、原理以及在单接与双接手机场景下的应用࿰…...
2025年能源电力系统与流体力学国际会议 (EPSFD 2025)
2025年能源电力系统与流体力学国际会议(EPSFD 2025)将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会,EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...
深入理解JavaScript设计模式之单例模式
目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式(Singleton Pattern&#…...
《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》
在注意力分散、内容高度同质化的时代,情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现,消费者对内容的“有感”程度,正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中࿰…...
【论文笔记】若干矿井粉尘检测算法概述
总的来说,传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度,通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...
什么是EULA和DPA
文章目录 EULA(End User License Agreement)DPA(Data Protection Agreement)一、定义与背景二、核心内容三、法律效力与责任四、实际应用与意义 EULA(End User License Agreement) 定义: EULA即…...
零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)
本期内容并不是很难,相信大家会学的很愉快,当然对于有后端基础的朋友来说,本期内容更加容易了解,当然没有基础的也别担心,本期内容会详细解释有关内容 本期用到的软件:yakit(因为经过之前好多期…...
docker 部署发现spring.profiles.active 问题
报错: org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
解读《网络安全法》最新修订,把握网络安全新趋势
《网络安全法》自2017年施行以来,在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂,网络攻击、数据泄露等事件频发,现行法律已难以完全适应新的风险挑战。 2025年3月28日,国家网信办会同相关部门起草了《网络安全…...
DBLP数据库是什么?
DBLP(Digital Bibliography & Library Project)Computer Science Bibliography是全球著名的计算机科学出版物的开放书目数据库。DBLP所收录的期刊和会议论文质量较高,数据库文献更新速度很快,很好地反映了国际计算机科学学术研…...

