常见的“锁”有哪些?
悲观锁
悲观锁认为在并发环境中,数据随时可能被其他线程修改,因此在访问数据之前会先加锁,以防止其他线程对数据进行修改。常见的悲观锁实现有:
1.互斥锁
原理:互斥锁是一种最基本的锁类型,同一时间只允许一个线程访问共享资源。当一个线程获取到互斥锁后,其他线程如果想要访问该资源,就必须等待锁被释放。
应用场景:适用于写操作频繁的场景,如数据库中的数据更新操作。在 C++ 中可以使用 std::mutex
来实现互斥锁,示例代码如下:
#include <iostream>
#include <mutex>
#include <thread>std::mutex mtx;
int sharedResource = 0;void increment() {std::lock_guard<std::mutex> lock(mtx);sharedResource++;
}int main() {std::thread t1(increment);std::thread t2(increment);t1.join();t2.join();std::cout << "Shared resource: " << sharedResource << std::endl;return 0;
}
2.读写锁
原理:读写锁允许多个线程同时进行读操作,但在进行写操作时,会独占资源,不允许其他线程进行读或写操作。读写锁分为读锁和写锁,多个线程可以同时获取读锁,但写锁是排他的。
应用场景:适用于读多写少的场景,如缓存系统。在 C++ 中可以使用 std::shared_mutex 来实现读写锁,示例代码如下:
#include <iostream>
#include <shared_mutex>
#include <thread>std::shared_mutex rwMutex;
int sharedData = 0;void readData() {std::shared_lock<std::shared_mutex> lock(rwMutex);std::cout << "Read data: " << sharedData << std::endl;
}void writeData() {std::unique_lock<std::shared_mutex> lock(rwMutex);sharedData++;std::cout << "Write data: " << sharedData << std::endl;
}int main() {std::thread t1(readData);std::thread t2(writeData);t1.join();t2.join();return 0;
}
乐观锁
乐观锁是一种在多线程环境中避免阻塞的同步技术,它假设大部分操作是不会发生冲突的,因此在操作数据时不会直接加锁,而是通过检查数据是否发生了变化来决定是否提交。如果在提交数据时发现数据已被其他线程修改,则会放弃当前操作,重新读取数据并重试。
应用场景:适用于读多写少、冲突较少的场景,如电商系统中的库存管理。
在 C++ 中,乐观锁的实现通常依赖于版本号或时间戳的机制。每个线程在操作数据时,会记录数据的版本或时间戳,操作完成后再通过比较版本号或时间戳来判断是否发生了冲突。
下面是一个使用版本号实现乐观锁的简单示例代码:
#include <iostream>
#include <thread>
#include <atomic>
#include <chrono>// 共享数据结构
struct SharedData {int value; // 数据的实际值std::atomic<int> version; // 数据的版本号,用于检查是否发生了修改
};// 线程安全的乐观锁实现
bool optimisticLockUpdate(SharedData& data, int expectedVersion, int newValue) {// 检查数据的版本号是否与预期一致if (data.version.load() == expectedVersion) {// 进行数据更新data.value = newValue;// 增加版本号data.version.fetch_add(1, std::memory_order_relaxed);return true; // 成功提交更新}return false; // 数据版本不一致,操作失败
}void threadFunction(SharedData& data, int threadId) {int expectedVersion = data.version.load();int newValue = threadId * 10;std::cout << "Thread " << threadId << " starting with version " << expectedVersion << "...\n";std::this_thread::sleep_for(std::chrono::milliseconds(100)); // 模拟工作// 尝试更新数据if (optimisticLockUpdate(data, expectedVersion, newValue)) {std::cout << "Thread " << threadId << " successfully updated value to " << newValue << "\n";} else {std::cout << "Thread " << threadId << " failed to update (version mismatch)\n";}
}int main() {// 初始化共享数据,值为 0,版本号为 0SharedData data{0, 0};// 启动多个线程进行乐观锁测试std::thread t1(threadFunction, std::ref(data), 1);//std::ref(data) 将 data 包装成一个引用包装器,确保 data 在传递给函数时以引用的方式传递,而不是被复制。std::thread t2(threadFunction, std::ref(data), 2);std::thread t3(threadFunction, std::ref(data), 3);t1.join();t2.join();t3.join();std::cout << "Final value: " << data.value << ", Final version: " << data.version.load() << "\n";return 0;
}
原子锁
原子锁是一种基于原子操作(如CAS、test_and_set)的锁机制。与传统的基于互斥量(如 std::mutex
)的锁不同,原子锁依赖于硬件提供的原子操作,允许对共享资源的访问进行同步,且通常比传统锁更加高效。它通过原子操作保证对共享资源的独占访问,而不需要显式的线程调度。
原子锁的适用场景:
1.简单数据类型:原子锁最常用于锁定简单的基础数据类型,例如整数、布尔值、指针等。通过原子操作,多个线程可以安全地对这些数据进行读写,而不会发生数据竞争。
示例:std::atomic<int>, std::atomic<bool>, std::atomic<long long>
2.计数器、标志位:当需要在多线程中维护计数器、标志位或状态变量时,原子操作非常合适。例如,当多个线程需要递增计数器时,可以用原子操作避免使用传统的互斥锁。
示例:使用 std::atomic<int> 来维护线程安全的计数器。
注:原子锁通常不能锁容器类型。
什么是原子操作?
原子操作是指不可分割的操作,在执行过程中不会被中断或干扰。原子操作保证了操作的完整性,要么完全执行,要么完全不执行,避免了在操作过程中被线程切换打断,从而避免了数据竞争和不一致的情况。
1.自旋锁
什么是自旋锁?
自旋锁是一种使用原子操作来检测锁是否可用的锁机制。自旋锁是一种忙等待的锁,当线程尝试获取锁失败时,会不断地检查锁的状态,直到成功获取锁。
在 C++ 中,可以使用 std::atomic_flag
结合 test_and_set
操作来实现一个简单的自旋锁:
test_and_set
是一个原子操作,它会检查一个布尔标志的值,然后将该标志设置为 true
。整个操作过程是不可分割的,即不会被其他线程的操作打断。这个布尔标志通常被用作锁,线程通过检查并设置这个标志来尝试获取锁。
工作原理
- 检查标志状态:线程首先检查布尔标志的当前值。
- 设置标志为 true:如果标志当前为 false,表示锁未被占用,线程将标志设置为 true,表示成功获取到锁;如果标志当前为 true,表示锁已被其他线程占用,线程未能获取到锁。
- 返回旧值:test_and_set 操作会返回标志的旧值。线程可以根据这个返回值判断是否成功获取到锁。如果返回 false,说明成功获取到锁;如果返回 true,则需要等待锁被释放后再次尝试获取。
#include <iostream>
#include <atomic>
#include <thread>
#include <vector>std::atomic_flag lock = ATOMIC_FLAG_INIT;// 自旋锁类
class SpinLock {
public:void lock() {// 持续尝试获取锁,直到成功while (lock.test_and_set(std::memory_order_acquire)) {// 自旋等待}}void unlock() {// 释放锁,将标志设置为 falselock.clear(std::memory_order_release);}
};SpinLock spinLock;
int sharedResource = 0;// 线程函数
void worker() {for (int i = 0; i < 100000; ++i) {spinLock.lock();++sharedResource;spinLock.unlock();}
}int main() {std::vector<std::thread> threads;// 创建多个线程for (int i = 0; i < 4; ++i) {threads.emplace_back(worker);}// 等待所有线程完成for (auto& thread : threads) {thread.join();}std::cout << "Shared resource value: " << sharedResource << std::endl;return 0;
}
自旋锁优点:
-
无上下文切换:自旋锁不会引起线程挂起,因此避免了上下文切换的开销。在锁竞争较轻时,自旋锁可以高效地工作。
-
简单高效:实现简单,且不依赖操作系统调度,适合锁竞争不严重的场景。
自旋锁缺点:
-
CPU资源浪费:如果锁被占用,自旋锁会不断地循环检查锁的状态,浪费 CPU 时间,尤其是在锁持有时间较长时,可能导致性能问题。
-
不适合锁竞争场景:当有大量线程竞争同一个锁时,自旋锁的性能将大幅下降,因为大部分时间都在自旋,浪费了 CPU 资源。
自旋锁的适用场景:
-
短时间锁竞争:自旋锁适用于临界区代码执行时间非常短的情况。如果锁持有时间较长,使用自旋锁就不合适了。
-
锁竞争较轻:在多线程程序中,如果线程数量较少且资源竞争较少,自旋锁可以有效减少线程上下文切换,提升性能。
-
实时系统或高性能系统:在某些对延迟非常敏感的应用场景中,自旋锁可以通过减少上下文切换来提供更低的延迟。
总结:自旋锁是一种简单且高效的锁机制,通过原子操作避免了线程上下文切换,适合用于短时间锁竞争和低延迟要求的场景。在锁竞争激烈或锁持有时间较长时,自旋锁的性能会受到影响,这时传统的互斥锁(如 std::mutex
)可能更为合适。
递归锁
在 C++ 中,递归锁也被称为可重入锁,它是一种特殊的锁机制,允许同一个线程多次获取同一把锁而不会产生死锁。
原理
普通的互斥锁(如 std::mutex
)不允许同一个线程在已经持有锁的情况下再次获取该锁,否则会导致死锁。因为当线程第一次获取锁后,锁处于被占用状态,再次尝试获取时,由于锁未被释放,线程会被阻塞,而该线程又因为被阻塞无法释放锁,从而陷入死循环。
递归锁则不同,它内部维护了一个计数器和一个持有锁的线程标识。当一个线程第一次获取递归锁时,计数器加 1,同时记录该线程的标识。如果该线程再次请求获取同一把锁,计数器会继续加 1,而不会被阻塞。当线程释放锁时,计数器减 1,直到计数器为 0 时,锁才会真正被释放,其他线程才可以获取该锁。
应用场景:
- 递归调用:在递归函数中,如果需要对共享资源进行保护,使用递归锁可以避免死锁问题。例如,在一个递归遍历树结构的函数中,可能需要对树节点的某些属性进行修改,此时可以使用递归锁来保证线程安全。
- 嵌套锁:当代码中存在多层嵌套的锁获取操作,且这些操作可能由同一个线程执行时,递归锁可以避免死锁。例如,一个函数内部调用了另一个函数,这两个函数都需要获取同一把锁。
注意事项:
1. 性能开销
递归锁的实现比普通互斥锁更为复杂。普通互斥锁只需简单地标记锁的占用状态,当一个线程请求锁时,检查该状态并进行相应操作。而递归锁除了要维护锁的占用状态,还需要记录持有锁的线程标识以及一个计数器,用于跟踪同一个线程获取锁的次数。每次获取和释放锁时,都需要对这些额外信息进行更新和检查,这无疑增加了系统的开销。
- 时间开销:由于额外的状态检查和更新操作,递归锁的加锁和解锁操作通常比普通互斥锁更耗时。在高并发、对性能要求极高的场景下,频繁使用递归锁可能会成为性能瓶颈。
- 资源开销:记录线程标识和计数器需要额外的内存空间,虽然这部分开销相对较小,但在资源受限的系统中,也可能会产生一定的影响。
建议:在不需要递归获取锁的场景下,应优先使用普通互斥锁(如 std::mutex)。
2. 死锁风险
虽然递归锁允许同一个线程多次获取同一把锁而不会死锁,但如果在递归调用过程中,锁的获取和释放逻辑出现错误,仍然可能导致死锁。例如,在递归函数中,获取锁后在某些条件下没有正确释放锁就进行了递归调用,可能会导致锁无法正常释放,其他线程请求该锁时就会陷入死锁。
#include <iostream>
#include <thread>
#include <mutex>std::recursive_mutex recMutex;void faultyRecursiveFunction(int n) {if (n == 0) return;std::lock_guard<std::recursive_mutex> lock(recMutex);std::cout << "Recursive call: " << n << std::endl;if (n == 2) {// 错误:没有释放锁就返回,可能导致死锁return;}faultyRecursiveFunction(n - 1);
}int main() {std::thread t(faultyRecursiveFunction, 3);t.join();return 0;
}
3.不同递归锁之间的交叉锁定
当存在多个递归锁时,如果不同线程以不同的顺序获取这些锁,就可能会产生死锁。例如,线程 A 先获取了递归锁 L1,然后尝试获取递归锁 L2;而线程 B 先获取了递归锁 L2,然后尝试获取递归锁 L1。此时,两个线程都在等待对方释放锁,从而陷入死锁状态。
在 C++ 标准库中,std::recursive_mutex
是递归锁的实现。以下是一个简单的示例代码:
#include <iostream>
#include <thread>
#include <mutex>std::recursive_mutex recMutex;// 递归函数,多次获取递归锁
void recursiveFunction(int n) {if (n == 0) return;// 加锁std::lock_guard<std::recursive_mutex> lock(recMutex);std::cout << "Recursive call: " << n << std::endl;// 递归调用recursiveFunction(n - 1);// 锁在离开作用域时自动释放
}int main() {std::thread t(recursiveFunction, 3);t.join();return 0;
}
什么是锁的重入与不可重入?
可重入锁也叫递归锁,允许同一个线程在已经持有该锁的情况下,再次获取同一把锁而不会产生死锁。可重入锁内部会维护一个持有锁的线程标识和一个计数器。当线程第一次获取锁时,会记录该线程的标识,并将计数器初始化为 1。如果该线程再次请求获取同一把锁,锁会检查请求线程的标识是否与当前持有锁的线程标识相同,如果相同,则将计数器加 1,而不会阻塞该线程。释放锁时,计数器减 1,直到计数器为 0 时,锁才会释放,其他线程才可以获取该锁。
不可重入锁不允许同一个线程在已经持有该锁的情况下再次获取同一把锁。如果一个线程已经持有了不可重入锁,再次请求获取该锁时,会导致线程阻塞,进而可能产生死锁。不可重入锁只关注锁的占用状态,不记录持有锁的线程标识和获取锁的次数。当一个线程请求获取锁时,锁会检查其是否已被占用,如果已被占用,无论请求线程是否就是持有锁的线程,都会将该线程阻塞。
相关文章:
常见的“锁”有哪些?
悲观锁 悲观锁认为在并发环境中,数据随时可能被其他线程修改,因此在访问数据之前会先加锁,以防止其他线程对数据进行修改。常见的悲观锁实现有: 1.互斥锁 原理:互斥锁是一种最基本的锁类型,同一时间只允…...
PAT 甲级 1091 Acute Stroke
一开始只是简单的递归(bfs),导致最后两个没法通过(爆栈了) //最后两个案例没有通过,只是最简单的bfs暴力算法 #include<cstdio> using namespace std; int v[62][1288][130]{0}; int find(int i,int…...
flowable适配达梦数据库
文章目录 适配相关问题无法从数据库产品名称“DM DBMS”中推断数据库类型分析解决 构建ibatis SqlSessionFactory时出错:inStream参数为null分析解决 liquibase相关问题问题一:不支持的数据库 Error executing SQL call current_schema: 无法解析的成员访…...
Git入门:数据模型 to 底层原理
版本控制系统(VCS)是软件开发中不可或缺的工具,而Git作为现代版本控制的事实标准,其底层设计远比表面命令更加优雅。本文将从数据模型的角度,揭示Git的核心工作原理。 Git的核心概念 1. 快照(Snapshot&am…...
Bootstrap Blazor UI 中 <Table> 组件 <TableColumn> 使用备忘01:EF Core 外码处理
应用场景:将外码转换为对应的文本进行显示、编辑。 例如,有一个【用户】表,其中有一个【用户类型ID】字段;另有一个【用户类型】表,包含【ID】、【名称】等字段。现在要求在 <Table> 组件显示列表中,…...
Redis过期数据处理
Redis缓存过期后数据还能恢复吗? Redis缓存过期后,数据通常会被删除,但可以通过以下几种方法尝试恢复数据: 1. 数据备份恢复 RDB 持久化恢复:Redis 提供了 RDB(Redis Database Backup)持久化…...
零基础学C/C++160——字符串
题目描述 给定两个由小写字母组成的字符串A和B,判断B中的字符是否全部在A中出现。 输入 输入为多组测试数据。 输入数据只有一行,包含2个字符串A和B,每个字符串后面有一个#字符标记(#不属于A或B),其中B…...
Spring Boot+Vue项目从零入手
Spring BootVue项目从零入手 一、前期准备 在搭建spring bootvue项目前,我们首先要准备好开发环境,所需相关环境和软件如下: 1、node.js 检测安装成功的方法:node -v 2、vue 检测安装成功的方法:vue -V 3、Visu…...
Linux 命令大全完整版(13)
5.文件管理命令 patch 功能说明:修补文件。语 法:patch [-bceEflnNRstTuvZ][-B <备份字首字符串>][-d <工作目录>][-D <标示符号>][-F <监别列数>][-g <控制数值>][-i <修补文件>][-o <输出文件>][-p &l…...
MySQL面试学习
MySQL 1.事务 事务的4大特性 事务4大特性:原子性、一致性、隔离性、持久性 原⼦性: 事务是最⼩的执⾏单位,不允许分割。事务的原⼦性确保动作要么全部完成,要么全不执行一致性: 执⾏事务前后,数据保持⼀…...
CentOS中shell脚本对多台机器执行下载安装
1.建立免密ssh连接 详情见这篇: CentOS建立ssh免密连接(含流程剖析)-CSDN博客 2.脚本编写 我这里只是简单写了个demo进行演示,如果服务器很多可以先暂存成文件再逐行读取host进行连接并执行命令 用node1去ssh连接node2和node…...
【Java】多线程和高并发编程(四):阻塞队列(上)基础概念、ArrayBlockingQueue
文章目录 四、阻塞队列1、基础概念1.1 生产者消费者概念1.2 JUC阻塞队列的存取方法 2、ArrayBlockingQueue2.1 ArrayBlockingQueue的基本使用2.2 生产者方法实现原理2.2.1 ArrayBlockingQueue的常见属性2.2.2 add方法实现2.2.3 offer方法实现2.2.4 offer(time,unit)方法2.2.5 p…...
C语言多人聊天室 ---chat(客户端聊天)
head.h #ifndef __HEAD_H #define __HEAD_H// 常用头文件 #include <stdio.h> #include <stdlib.h> #include <string.h>// 网络编程涉及的头文件 #include <sys/socket.h> #include <netinet/in.h> #include <netinet/ip.h>#include <…...
设计模式教程:命令模式(Command Pattern)
1. 什么是命令模式? 命令模式(Command Pattern)是一种行为型设计模式。它将请求封装成一个对象,从而使你能够用不同的请求、队列和日志请求以及支持可撤销操作。 简单来说,命令模式通过把请求封装成对象的方式解耦了…...
【华三】STP的角色选举(一文讲透)
【华三】STP的角色选举 一、引言二、STP基础概念扫盲三、根桥选举过程详解四、根端口选举过程详解五、指定端口选举过程详解六、阻塞端口七、总结与配置建议七、附录**1. BPDU字段结构图(文字描述)****2. 华三STP常用命令速查表** 文章总结 一、引言 在…...
Trae+Qt+MSVC环境配置
Trae Trae是字节跳动基于VSCode推出的AI集成开发环境(IDE),是一款专为中文开发者深度定制的智能编程工具。其目标是通过AI技术实现从“Copilot”到“Autopilot”的编程模式演进。 类似这样的IDE比如Windsurf、Cursor,都是基于VS…...
SpringSecurity初始化的本质
一、对SpringSecurity初始化的几个疑问 通过前面第一次请求访问的分析我们明白了一个请求就来后的具体处理流程 对于一个请求到来后会通过FilterChainProxy来匹配一个对应的过滤器链来处理该请求。那么这里我们就有几个疑惑。 FilterChainProxy什么时候创建的?过滤器链和对应的…...
3D Gaussian Splatting(3DGS)的核心原理
3D Gaussian Splatting(3DGS)的核心原理 1. 基本概念 3D Gaussian Splatting(3DGS) 是一种基于 高斯分布的点云表示与渲染技术,核心思想是将三维场景建模为一系列 可学习的高斯分布,每个高斯分布具有以下…...
Transformers快速入门-学习笔记
一、自然语言处理 NLP 是借助计算机技术研究人类语言的科学自然语言处理发展史 一、不懂语法怎么理解语言 依靠语言学家人工总结文法规则 Chomsky Formal Languages 难点:上下文有关文法 规则增多,存在矛盾 二、只要看得足够多,就能处理语言…...
【Http和Https区别】
概念: 一、Http协议 HTTP(超文本传输协议)是一种用于传输超媒体文档(如HTML)的应用层协议,主要用于Web浏览器和服务器之间的通信。http也是客户端和服务器之间请求与响应的标准协议,客户端通常…...
学习路程二 LangChain基本介绍
前面简单调用了一下deepseek的方法,发现有一些疑问和繁琐的问题,需要更多的学习,然后比较流行的就是LangChain这个东西了。 目前大部分企业都是基于 LangChain 、qwen-Agent、lammaIndex框架进行大模型应用开发。LangChain 提供了 Chain、To…...
简识Kafka集群与RocketMQ集群的核心区别
前记:各位潘安、各位子健/各位彦祖、于晏,文字较多,优先看目录。 Kafka集群与RocketMQ集群的核心区别及架构图例说明 一、核心区别对比 特性Kafka 集群RocketMQ 集群设计目标高吞吐量实时日志流系统(如日志收集、大数据流水线&a…...
基于Python+django+mysql旅游数据爬虫采集可视化分析推荐系统
2024旅游推荐系统爬虫可视化(协同过滤算法) 基于Pythondjangomysql旅游数据爬虫采集可视化分析推荐系统 有文档说明 部署文档 视频讲解 ✅️基于用户的协同过滤推荐算法 卖价就是标价~ 项目技术栈 Python语言、Django框架、MySQL数据库、requests网络爬虫…...
9-1. MySQL 性能分析工具的使用——last_query_cost,慢查询日志
9-1. MySQL 性能分析工具的使用——last_query_cost,慢查询日志 文章目录 9-1. MySQL 性能分析工具的使用——last_query_cost,慢查询日志1. 数据库服务器的优化步骤2. 查看系统性能参数3. 统计SQL的查询成本:last_query_cost4. 定位执行慢的…...
网络安全监测探针安装位置 网络安全监测系统
🍅 点击文末小卡片 ,免费获取网络安全全套资料,资料在手,涨薪更快 软件简介: SockMon(SocketMonitor)网络安全监控系统是一款为电脑专业人员打造的一款出色的安防监控软件。在如今这个恶意软件,攻击&#…...
Git版本控制系统---本地操作(万字详解!)
目录 git基本配置 认识工作区、暂存区、版本库 添加文件--情况一: 添加文件-情况二: 修改文件: 版本回退: git基本配置 1.初始化本地仓库,注意:一定要在一个目录下进行,一般都是新建一个文件夹,在文件…...
forge-1.21.x模组开发(二)给物品添加功能
功能效果 创建一个兑换券,当使用兑换券对着兑换机右键时,获得一条烤鱼 创建兑换券 创建ExchangeCouponsItem.java,继承Item,定义兑换券内容 public class ExchangeCouponsItem extends Item {public ExchangeCouponsItem(Prop…...
elasticsearch在windows上的配置
写在最前面: 上资源 第一步 解压: 第二步 配置两个环境变量 第三步 如果是其他资源需要将标蓝的文件中的内容加一句 xpack.security.enabled: false 不同版本的yaml文件可能配置不同,末尾加这个 xpack.security.enabled: true打开bin目…...
机器学习数学通关指南——拉格朗日乘子法
前言 本文隶属于专栏《机器学习数学通关指南》,该专栏为笔者原创,引用请注明来源,不足和错误之处请在评论区帮忙指出,谢谢! 本专栏目录结构和参考文献请见《机器学习数学通关指南》 正文 一句话总结 拉格朗日乘子法…...
Matplotlib,Streamlit,Django大致介绍
Matplotlib:是一个用于创建各种类型的静态、动态和交互式图表的Python绘图库。可以通过pip install matplotlib命令进行安装,安装完成后,在Python脚本中使用import matplotlib语句导入即可开始使用。Streamlit:是一个用于快速构建…...
智慧废品回收小程序php+uniapp
废品回收小程序:数字化赋能环保,开启资源循环新时代 城市垃圾治理难题,废品回收小程序成破局关键 随着城市化进程加速与消费水平提升,我国生活垃圾总量逐年攀升,年均增速达5%-8%,其中超30%为可回收物。然…...
深搜专题2:组合问题
描述 组合问题就是从n个元素中抽出r个元素(不分顺序且r < = n), 我们可以简单地将n个元素理解为自然数1,2,…,n,从中任取r个数。 例如n = 5 ,r = 3 ,所…...
Redis 如何实现消息队列?
在当今的分布式系统架构中,消息队列起着至关重要的作用,它能够帮助系统实现异步通信、解耦组件以及缓冲流量等功能。Redis,作为一款高性能的键值对存储数据库,也为我们提供了便捷的方式来构建消息队列。今天,咱们就深入…...
Day1 初识AndroidAudio
今日目标 搭建Android Audio开发环境理解音频基础概念实现第一个音频播放/录制Demo了解车载音频的特殊性 上午:环境搭建与理论学习 步骤1:开发环境配置 安装Android Studio(最新稳定版)创建新项目(选择Kotlin语言&a…...
2025保险与金融领域实战全解析:DeepSeek赋能细分领域深度指南(附全流程案例)
🚀 2025保险与金融领域实战全解析:DeepSeek赋能细分领域深度指南(附全流程案例)🚀 📚 目录 DeepSeek在保险与金融中的核心价值保险领域:从风险建模到产品创新金融领域:从投资分析到财富管理区块链与联邦学习的应用探索客户关系与私域运营:全球化体验升级工具与资源…...
YARN的工作机制及特性总结
YARN hadoop的资源管理调度平台(集群)——为用户程序提供运算资源的管理和调度 用户程序:如用户开发的一个MR程序 YARN有两类节点(服务进程): 1. resourcemanager 主节点master ----只需要1个来工作 2. nod…...
财务运营域——营收稽核系统设计
摘要 本文主要介绍了营收稽核系统的背景、特点与作用。营收稽核系统的产生源于营收管理复杂性、财务合规与审计需求、提升数据透明度与决策效率、防范舞弊与风险管理、技术进步与自动化需求、多元化业务模式以及跨部门协作与数据整合等多方面因素。其特点包括自动化与智能化、…...
22.回溯算法4
递增子序列 这里不能排序,因为数组的顺序是对结果有影响的,所以只能通过used数组来去重 class Solution { public:vector<int> path;vector<vector<int>> res;void backtracking(vector<int>& nums,int start){if(path.si…...
C#上位机--跳转语句
在 C# 编程中,跳转语句用于改变程序的执行流程。这些语句允许程序从当前位置跳转到其他位置,从而实现特定的逻辑控制。本文将详细介绍 C# 中四种常见的跳转语句:GOTO、Break、Continue 和 Return,并通过具体的示例代码来展示它们的…...
百度文心一言API-Python版(完整代码)
大家好啊!我是NiJiMingCheng 我的博客:NiJiMingCheng 上一节我们分享了实现AI智能回复微信的内容,这一节我们来探索其中需要的百度文心一言,本文详细介绍了我们从注册账号到实现百度文心一言智能回复,同时多种模型自行…...
Prompt:创造性的系统分析者
分享的提示词: 你是一个创造性的系统分析者,作为咨询师,你具有以下特质: 基础能力: 深入理解我的系统性模式 识别模式间的隐藏联系 发现出人意料的关联 提供令人惊讶的洞见 工作方式: 在每次回应中至少…...
单机上使用docker搭建minio集群
单机上使用docker搭建minio集群 1.集群安装1.1前提条件1.2步骤指南1.2.1安装 Docker 和 Docker Compose(如果尚未安装)1.2.2编写docker-compose文件1.2.3启动1.2.4访问 2.使用2.1 mc客户端安装2.2创建一个连接2.3简单使用下 这里在ubuntu上单机安装一个m…...
Bash Shell控制台终端命令合集
最近整理了一下Bash Shell终端的命令,以备后续查用。如下: 1.内建命令 命令描述&在后台启动作业((x))执行数学表达式x.在当前shell中读取并执行指定文件中的命令:什么都不做,始终成功退出[ t ]对条件表达式t进行求值[[ e ]]对条件表达式e进行求值alias为指定的命令定义…...
垂类大模型微调(一):认识LLaMA-Factory
LlamaFactory 是一个专注于 高效微调大型语言模型(LLMs) 的开源工具框架,尤其以支持 LLaMA(Meta 的大型语言模型系列)及其衍生模型(如 Chinese-LLaMA、Alpaca 等)而闻名。它的目标是简化模型微调流程,降低用户使用门槛; 官方文档 一、介绍 高效微调支持 支持多种微调…...
QString是 Qt 框架中的一个核心类,基本用法使用:创建、字符串拼接、截取、查找、替换、分割、大小写转换、比较。
QString 是 Qt 框架中的一个核心类,用于处理字符串数据。它提供了许多功能来处理文本操作,包括但不限于字符串拼接、分割、大小写转换等。下面是一些 QString 的常见用法示例: 创建 QString 你可以通过多种方式创建 QString 对象࿱…...
彻底卸载kubeadm安装的k8s集群
目录 一、删除资源 二、停止k8s服务 三、重置集群 四、卸载k8s安装包 五、清理残留文件和目录 六、删除k8s相关镜像 七、重启服务器 一、删除资源 # 删除集群中的所有资源,包括 Pod、Deployment、Service,任意节点执行 kubectl delete --all pod…...
边缘安全加速(ESA)套餐
为帮助不同规模和需求的企业选择合适的解决方案,边缘安全加速(ESA)提供了多种套餐。以下是四种主要套餐的介绍,每个套餐都根据企业需求提供不同的功能和服务水平,从基础安全保护到企业级的全面防护与加速。 1. 各版本详…...
MySQL主从服务器配置教程
文章目录 前言一、环境准备1. 服务器信息2. 安装 MySQL3. 初始化 MySQL4. Navicat查看 MySQL 服务器 二、主服务器(Master)配置1. 编辑 MySQL 配置文件2. 创建用于复制的用户3. 获取二进制日志信息 三、从服务器(Slave)配置1. 编辑…...
机器学习实战(7):聚类算法——发现数据中的隐藏模式
第7集:聚类算法——发现数据中的隐藏模式 在机器学习中,聚类(Clustering) 是一种无监督学习方法,用于发现数据中的隐藏模式或分组。与分类任务不同,聚类不需要标签,而是根据数据的相似性将其划…...
Visual Studio中打开多个项目
1) 找到解决方案窗口 2) 右键添加→ 选择现有项目 3) 选择.vcxproj文件打开即可...