C++多线程编码二
1.lock和try_lock
lock是一个函数模板,可以支持多个锁对象同时锁定同一个,如果其中一个锁对象没有锁住,lock函数会把已经锁定的对象解锁并进入阻塞,直到多个锁锁定一个对象。
try_lock也是一个函数模板,尝试对多个锁对象进行同时尝试锁定,如果锁对象全部锁定,返回-1,如果某一个锁对象尝试锁定是失败,把已经锁定成功的锁对象解锁,并返回这个对象的下标(第一个参数对象,下标从0开始)
代码示例
template<class Mutex1,class Mutex2,class... Mutexes>
void lock(Mutex1& a, Mutex2& b, Mutexes&... cde);template<class Mutex1,class Mutex2,class... Mutexes>
int tyr_lock(Mutex1& a, Mutex2& b, Mutexes&... cde);#include<iostream>
#include<thread>
#include<mutex>
using namespace std;std::mutex foo, bar;void task_a()
{std::lock(foo, bar);std::cout << "task a" << endl;foo.unlock();bar.unlock();
}void task_b()
{std::lock(bar,foo);std::cout << "task b" << std::endl;foo.unlock();bar.unlock();
}int main()
{foo.lock();std::thread t1(task_a);std::thread t2(task_b);std::cout << "xxxxxxxxxxx" << endl;bar.lock();foo.unlock();std::cout << "yyyyyyyyyy" << endl;bar.unlock();t1.join();t2.join();return 0;
}
代码解释:
主线程先锁定了foo,然后走两个线程任务,因为foo锁已经被申请了,两个线程就会阻塞在std::lock这里,主线程就会继续执行,锁定bar,然后解锁foo。两个线程可以申请foo锁资源了,但是bar申请不到,所以两个线程还是阻塞住,然后主线程申请bar锁并释放了foo锁,这两个线程还是会阻塞,task_b虽然拿到了bar锁,但是foo锁拿不到就会释放掉bar锁,重新申请这两个锁,所以主线程就能拿到bar锁,打印yyyy,然后释放bar锁,这时候,线程就开始争夺这两个锁资源,进而执行线程任务。
代码二
template<class Mutex1,class Mutex2,class... Mutexes>
void lock(Mutex1& a, Mutex2& b, Mutexes&... cde);template<class Mutex1,class Mutex2,class... Mutexes>
int tyr_lock(Mutex1& a, Mutex2& b, Mutexes&... cde);#include<iostream>
#include<thread>
#include<mutex>
using namespace std;std::mutex foo, bar;void task_a()
{foo.lock();std::cout << "task a\n";bar.lock();foo.unlock();bar.unlock();
}void task_b()
{int x = try_lock(bar, foo);if (x == -1){std::cout << "task b\n";bar.unlock();foo.unlock();}else{std::cout << "task b failed:mutex:" << (x ? "foo" : "bar") << endl;}}int main()
{std::thread t1(task_a);std::thread t2(task_b);t1.join();t2.join();return 0;
}
代码解释:
首先线程先执行a任务,先拿foo锁,然后再那bar锁,然后把拿到的锁都释放了,b任务是尝试申请foo和bar锁,如果锁都没拿到就返回-1,存在没申请到的就返回下标。这时就会存在两种结果,如果a任务快的话,把两个锁都拿了,那么任务b执行if,如果a任务拿到锁并释放了,b任务执行else
结果一
结果二
2.call_once函数
多线程执行时,让第一个线程执行任务,其它线程不再执行这个任务。(多线程去争夺唯一机会且只执行一次任务)
template< class Function, class... Args >
void call_once( std::once_flag& flag, Function&& f, Args&&... args );
-
std::once_flag
:一个标志,用于记录函数是否已经被调用过。 -
Function
:要执行的函数。 -
Args...
:传递给函数的参数。
std::once_flag winner_flag;
-
std::once_flag
是一个无状态的标志,用于记录std::call_once
是否已经被调用。 -
它不需要显式初始化,只需要声明即可。
代码示例
template<class Mutex1,class Mutex2,class... Mutexes>
void lock(Mutex1& a, Mutex2& b, Mutexes&... cde);template<class Mutex1,class Mutex2,class... Mutexes>
int tyr_lock(Mutex1& a, Mutex2& b, Mutexes&... cde);#include<iostream>
#include<thread>
#include<mutex>
#include<chrono>
using namespace std;std::mutex foo, bar;int winner;
void set_winner(int x) { winner = x; }
std::once_flag winner_flag;void wait_time(int id)
{for (int i = 0; i < 1000; i++){std::this_thread::sleep_for(std::chrono::milliseconds(1));}std::call_once(winner_flag, set_winner, id);
}int main()
{std::thread threads[10];for (int i = 0; i < 10; i++){threads[i] =std::thread(wait_time, i + 1);}for (auto& s : threads){s.join();}std::cout << "winner thread:" << winner << endl;return 0;
}
代码解释:
主线程创建10个线程,然后都执行一个函数,并设置id号,进入到wait函数都要进行休眠,然后公平竞争唯一机会,休眠结束后,同一执行任务call函数,只有一个线程会进入到set函数,并改变winner_flag标志,使其它线程进不到这个函数,打印id知道哪一个线程执行了这个任务。
3.atomic
atomic是一个模板的实例化和全特化定义的原子类型,保证对一个原子对象的操作是线程安全的。
load和store可以原子的读取和修改atomic封装存储的T对象。
atomic的原理主要是硬件层面支持,现代处理器提供了原子指令来支持原子操作。如x86架构,有CMPXCHG(比较并交换)指令。这些原子指令能够在一个不可分割的操作中完成对内存的读取,比较和写入操作,简称CAS,Compare And Set,或是Compare And Swap。另外为了处理多个处理器缓存之间的数据一致性问题,硬件采用了缓存一致性协议,当一个atomic操作修改了一个变量的值,缓存一致性协议会确保其它处理器缓存中相同的变量副本被正确的更新还在标记为无效。
template < class T >bool atomic_compare_exchange_weak (atomic<T>* obj, T* expected, T val)noexcept ;template < class T >bool atomic_compare_exchange_strong (atomic<T>* obj, T* expected, T val)noexcept ;// C++11 中 atomic 类的成员函数bool compare_exchange_weak (T& expected, T val,memory_order sync = memory_order_seq_cst) noexcept ;bool compare_exchange_strong (T& expected, T val,memory_order sync = memory_order_seq_cst) noexcept ;
C++11的CAS操作支持,atomic对象跟expected按位比较相等,则用val更新atomic对象并返回值true,若atomic对象跟expected按位比较不相等,则更新expected为当前的atomic对象并返回false。(就是多个线程进来,每个线程都有一样的初始值,总有先后,先的改了值,回来的修改值但要先比较初始值是否相等,不相等就说明被改了,就要更换初始值,然后再来一次,只有相等才可以修改这个值)
compare_exchange_weak在某些平台上,即使原子变量的值等于expected,也可能"虚假"的返回。这是因为底层硬件和编译器优化导致的。compare_exchange_strong是不会的,但是有代价(硬件的缓存一致性协议EMSI),所以一般是weak,在考虑性能的情况下。安全情况下可以用strong/。
C++11标准库中,std::atomic提供了多种内存顺序(memory_order)选项,用于控制原子操作的内存同步行为。这些顺序选项允许开发者在性能与正确性之间进行权衡,特别是多线程编程中。
1.memory_order_relaxed最宽松的内存顺序,保证原子操作的原子性,不提供任何同步或者顺序约束。适用计数器。
std::atomic< int > x ( 0 );x. store ( 42 , std::memory_order_relaxed); // 仅保证原⼦性
2.memory_order_consume
3.memory_order_acquire保证当前操作之前的所有读写操作(在当前线程中)不会被重排序到当
template<class Mutex1,class Mutex2,class... Mutexes>
void lock(Mutex1& a, Mutex2& b, Mutexes&... cde);template<class Mutex1,class Mutex2,class... Mutexes>
int tyr_lock(Mutex1& a, Mutex2& b, Mutexes&... cde);#include<atomic>
#include<iostream>
#include<thread>
#include<mutex>
#include<chrono>
#include<vector>
using namespace std;std::mutex foo, bar;atomic<int> acnt;int cnt;void Add1(atomic<int>& cnt)
{int old = cnt.load();// 如果cnt的值跟old相等,则将cnt的值设置为old+1,并且返回true,这组操作是原⼦的。// 那么如果在load和compare_exchange_weak操作之间cnt对象被其他线程改了// 则old和cnt不相等,则将old的值改为cnt的值,并且返回false。while (!atomic_compare_exchange_weak(&cnt, &old, old + 1));}
void f()
{for (int i = 0; i < 100000; i++){++acnt;++cnt;}
}
int main()
{std::vector<thread> pool;for (int i = 0; i < 4; i++){pool.emplace_back(f);}for (auto& e : pool){e.join();}std::cout << "原子计数器:" << acnt << endl;cout << "非原子计数器:" << cnt << endl;return 0;
}
代码解释:
std::atomic
是 C++ 标准库中用于实现原子操作的模板类,它可以确保对变量的读写操作是原子性的,从而避免竞态条件(race conditions)。而全局变量cnt就会因为多线程并发导致数据不一致问题,计数不准确。atomic的原子操作就像Add1函数的实现,每次都要与旧值进行比较,只有相等才表示没有其它线程执行,就可以进行修改,而变了就会先修改旧值变为当前值,然后再循环判断是否与旧值相当,相等就可以进行修改了。
注意:这里不能使用push_back():
push_back
的工作原理是:
-
先构造一个临时对象。
-
然后将这个临时对象拷贝或移动到容器中。
但 std::thread
的拷贝构造函数是被删除的(delete
),这意味着你不能拷贝一个 std::thread
对象。因此,如果你尝试使用 push_back
,编译器会报错,因为 push_back
试图拷贝构造一个 std::thread
对象。
emplace_back
的工作原理是:
-
直接在容器的存储空间中构造对象。
-
避免了不必要的拷贝或移动。
因此,emplace_back
可以直接在 std::vector<std::thread>
中构造 std::thread
对象,而不需要先构造一个临时对象再移动。
在 C++ 中, 平凡可复制类型(Trivially Copyable Type) 是指可以安全地通过逐字节复制(如memcpy
或memmove
)来进行复制的类型。这种类型的对象在内存中的表示是连续的,并且复制操作不会导致未定义行为。
代码3
template<class Mutex1,class Mutex2,class... Mutexes>
void lock(Mutex1& a, Mutex2& b, Mutexes&... cde);template<class Mutex1,class Mutex2,class... Mutexes>
int tyr_lock(Mutex1& a, Mutex2& b, Mutexes&... cde);#include<atomic>
#include<iostream>
#include<thread>
#include<mutex>
#include<chrono>
#include<vector>
using namespace std;std::mutex foo, bar;atomic<int> acnt;struct Node { int val; Node* next; };std::atomic<Node*> list_head(nullptr);void append(int val, int n)
{for (int i = 0; i < n; i++){Node* oldHead = list_head;Node* newnode = new Node{ val + i,oldHead };while (!list_head.compare_exchange_weak(oldHead, newnode))newnode->next = oldHead;}
}int main()
{std::vector<std::thread> threads;threads.emplace_back(append, 0, 10);threads.emplace_back(append, 10, 10);threads.emplace_back(append, 20, 10);threads.emplace_back(append, 30, 10);threads.emplace_back(append, 40, 10);for (auto& e : threads){e.join();}for (Node* it = list_head; it != nullptr; it = it->next){std::cout << ' ' << it->val;}Node* it;while (it = list_head){list_head = it->next;delete it;}return 0;
}
代码解释:
全局创建原子对象,类型是Node*,头节点为空,append函数用来加入结点,oldHead=头节点,newnode是要链接的新结点,while循环保证原子操作,如果list_head的值与oldHead相等,就会把list_head的值变为newnode值,返回true;如果不相等,就把list_head的值变成oldHead的值,返回false,false取反为true,就会执行循环内容,新结点链接到链表。这样能保证原子性,因为只有头节点的值和oldHead相等,才能保证没有其它线程进入修改值,就可以放心链接。如果修改了,就说明有其它线程进入修改了头节点,而此线程的头节点没更新就会覆盖式插入进去,导致数据丢失了。
代码四
#include<atomic>
#include<iostream>
#include<thread>
#include<mutex>
//#include<chrono>
#include<vector>
using namespace std;template<class T>
struct node
{T data;node* next;node(const T& data):data(data),next(nullptr){}
};namespace lock_free
{template<class T>class stack{public:std::atomic<node<T>*> head = nullptr;void push(const T& data){node<T>* new_node = new node<T>(data);new_node->next = head.load(std::memory_order_relaxed);while (!head.compare_exchange_weak(new_node->next, new_node, std::memory_order_release, memory_order_relaxed));}};
}namespace lock
{template<class T>class stack{public:node<T>* head = nullptr;void push(const T& data){node<T>* new_node = new node<T>(data);new_node->next = head;head = new_node;}};
}int main()
{lock_free::stack<int> s1;lock::stack<int> s2;std::mutex mtx;int n = 1000000;auto lock_free_stack = [&s1, n]() {for (size_t i = 0; i < n; i++){s1.push(i);}};auto lock_stack = [&s2, n, &mtx]() {for (size_t i=0; i < n; i++){std::lock_guard < std::mutex > lock(mtx);s2.push(i);}};size_t begin1 = clock();std::vector<std::thread> threads1;for (size_t i=0; i < 4; i++){threads1.emplace_back(lock_free_stack);}for (auto& e : threads1){e.join();}size_t end1 = clock();std::cout << end1 - begin1 << endl;size_t begin2 = clock();std::vector<std::thread> threads2;for (size_t i = 0; i < 4; i++){threads2.emplace_back(lock_stack);}for (auto& e : threads2){e.join();}size_t end2 = clock();std::cout << end2 - begin2 << std::endl;return 0;
}
代码解释:
定义两种原子操作,一个是无锁模式,一个是有锁模式。无锁模式用compare_exchange_weak函数实现,定义原子对象头节点。无锁模式就是正常的插入结点。主函数中,定义了无锁对象和有锁对象,以及一把锁。接着实现了两个lambda分别对应有锁和无锁模式,接着用clock函数来计数时间差,获取消耗时间。
4.自旋锁实现
#include<atomic>
#include<iostream>
#include<thread>
#include<mutex>
//#include<chrono>
#include<vector>
using namespace std;class SpinLock
{
private:std::atomic_flag flag = ATOMIC_FLAG_INIT;
public:void Lock(){// test_and_set将内部值设置为true,并且返回之前的值// 第⼀个进来的线程将值原⼦的设置为true,返回false// 后⾯进来的线程将原⼦的值设置为true,返回true,所以卡在这⾥空转,// 直到第⼀个进去的线程unlock,clear,将值设置为falsewhile (flag.test_and_set());}void unlock(){// clear将值原⼦的设置为falseflag.clear();}
};void worker(SpinLock& lock, int& val)
{lock.Lock();for (int i = 0; i < 1000; i++){val++;}lock.unlock();
}int main()
{SpinLock lock;int val = 0;std::vector<thread> threads;for (int i = 0; i < 4; i++){threads.emplace_back(worker, std::ref(lock), std::ref(val));}for (auto& thread : threads){thread.join();}std::cout << "val:" << val << endl;return 0;
}
代码解释:
std::atomic_flag
是 C++ 标准库中的一个类型,它提供了一种轻量级的同步原语,通常用于实现锁或其他同步机制。std::atomic_flag
表示一个原子布尔变量,它可以处于两种状态之一:清零(false)或置一(true)。
自旋锁就是会卡在循环出不来,只有标志位改变了,才能出循环,第一个进入lock执行while就会改变标志位的值为true,只有解锁才能改变标志位的值为false,其它线程就会在while一直空转。
5.condition_variable
condition_variable需要配合互斥锁系列使用,主要提供wait和notify系统接口。
wait需要传递一个unique_lock<mutex>类型的互斥锁,wait会阻塞当前线程直到被notify。在进入阻塞的一瞬间,会解开互斥锁,给其它线程获取锁,访问条件变量。被notify唤醒,会继续获取锁,再继续往下执行。
notify_one会唤醒当前条件变量上等待的其中之一线程,使用时也需要用互斥锁保护,如果没有阻塞等待,就无事发生。notify_all会唤醒当前条件变量上等待的所有线程。
代码示例
#include<atomic>
#include<iostream>
#include<thread>
#include<mutex>
//#include<chrono>
#include<vector>
using namespace std;std::mutex mtx;
std::condition_variable cv;
bool ready = false;void print_id(int id)
{std::unique_lock<std::mutex> lck(mtx);while (!ready)cv.wait(lck);std::cout << "thread" << id << endl;
}void go()
{std::unique_lock<std::mutex> lck(mtx);ready = true;cv.notify_all();
}int main()
{std::thread threads[10];for (int i = 0; i < 10; i++){threads[i] = std::thread(print_id, i);}std::cout << "10 threads ready" << endl;std::this_thread::sleep_for(std::chrono::milliseconds(100));go();for (auto& e : threads){e.join();}return 0;
}
代码解释:
主线程创建的10个线程都会执行print函数,print函数会有wait挂起线程,并释放锁,所以每一个线程都能进来然后别挂起。主线程就延时保证全部线程都进入条件变量,再执行go函数,唤醒所有线程去争夺锁资源。
代码二两个线程交替打印奇数和偶数
#include<atomic>
#include<iostream>
#include<thread>
#include<mutex>
//#include<chrono>
#include<vector>
using namespace std;int main()
{std::mutex mtx;std::condition_variable cv;int n = 100;bool flag = true;std::thread t1([&]() {int i = 0;while (i < n){std::unique_lock<std::mutex> lock(mtx);while (!flag){cv.wait(lock);}cout << i << endl;flag = false;i += 2;cv.notify_one();}});std::thread t2([&]() {int j = 1;while (j < n){std::unique_lock<std::mutex> lock(mtx);while (flag){cv.wait(lock);}cout << j << endl;flag = true;j += 2;cv.notify_one();}});t1.join();t2.join();return 0;
}
代码解释:
两个线程一个打印偶数,一个打印奇数,用flag标志位来控制,开始为true,线程一就不会进入条件变量,而线程二会进入条件变量挂起,等待线程一唤醒,所以无论位置如何,都是线程一先打印,也不会出现一个线程结束唤醒其它线程,然后唤醒的线程没拿到锁,又被结束线程拿到,flag会解决问题。
相关文章:
C++多线程编码二
1.lock和try_lock lock是一个函数模板,可以支持多个锁对象同时锁定同一个,如果其中一个锁对象没有锁住,lock函数会把已经锁定的对象解锁并进入阻塞,直到多个锁锁定一个对象。 try_lock也是一个函数模板,尝试对多个锁…...
【最新版】金媒婚恋系统v10.5最新稳定开源+原生前端小程序 PC端+安装教程
一.系统简介 1. 红娘服务 红娘服务模块是该系统的一大特色。专业红娘会通过分析用户的个人资料和偏好, 为用户提供精准的配对建议和个性化服务。用户可以预约红娘服务,通过红娘的介入,提升配对成功率。 2. 相亲活动 相亲活动模块用于组织和管…...
[spring] spring AOP - 面向切面编程の学习
[spring] spring AOP - 面向切面编程の学习 几年前开始还在被 spring 的八股文时,AOP 就是一个比较热也比较大的点,为了面试确实背过不少,不过 AOP 实现本身做的不多,一方面也是因为 AOP 一旦配置好了基本上就不需要改什么&#…...
JavaScript 中的 Reflect 详解
Reflect 是 ES6引入的一个内置对象,它提供了一系列静态方法来操作对象,这些方法与 Proxy 处理器方法一一对应。Reflect 的设计目的是为了更优雅地操作对象,并统一某些操作的行为。 1. Reflect 的基本特点 1. 不是构造函数:不能使…...
【操作系统】linux常用命令
UP作为一个Linux系统练习两年半的个人练习生,今天分门别类地给大家整理一下常用的Linux命令,祝大家在Linux练习之路一帆风顺。 文件和目录操作 文件查看与编辑 文件查找 文件权限与所有权 进程管理 系统信息与监控 网络管理与诊断...
002 vue组件化编程
文章目录 一般方式全局组件局部组件 组件(Component)是Vue.js最强大的功能之一 组件也是一个Vue实例,也包括:data、methods、生命周期函数等 组件渲染需要html模板,所以增加了template属性,值就是HTML模板 …...
常见的 JavaScript 框架和库
在现代前端开发中,JavaScript框架和库成为了构建高效、可维护应用程序的关键工具。本文将介绍四个常见的JavaScript框架和库:React、Vue.js、Angular 和 Node.js,并探讨它们的特点、使用场景及适用场合。 1. React — 构建用户界面的JavaScri…...
005_循环结构
循环结构 循环结构的作用和应用场景for循环while循环for和while的使用规范do - while 死循环循环嵌套break、continueRandom生成随机数 循环结构的作用和应用场景 减少代码的重复编写、灵活的控制程序的执行 for循环 for (1初始化语句; 2循环条件; 3迭代语句){4循环体语句(重…...
1110+款专业网站应用程序UI界面设计矢量图标figma格式素材 Icon System | 1,100+ Icons Easily Customize
1110款专业网站应用程序UI界面设计矢量图标figma格式素材 Icon System | 1,100 Icons Easily Customize 产品特点 — 24 x 24 px 网格大小 — 2px 线条描边 — 所有形状都是基于矢量的 — 平滑和圆角 — 易于更改颜色 类别 🚨 警报和反馈 ⬆️ 箭头 &…...
leetcode 368. 最大整除子集 中等
给你一个由 无重复 正整数组成的集合 nums ,请你找出并返回其中最大的整除子集 answer ,子集中每一元素对 (answer[i], answer[j]) 都应当满足: answer[i] % answer[j] 0 ,或answer[j] % answer[i] 0 如果存在多个有效解子集&…...
项目总结之常问的一些问题
1.项目功能介绍,重难点 重难点: mock工具使用(涉及到的三方接口过多,由于网络等原因无法调通,所以测试的时候,采用mock工具来模拟返回接口真正调用后响应数据) 2.项目负责哪部分?…...
51单片机实现精确定时
一、51单片机代码 #include <reg52.h>sbit LED P3^3;extern bit b10Ms; extern bit b100Ms; extern bit b1S; extern bit b10S;void Timer0_Init() {TMOD | 0x01; // 设置定时器0为模式1TH0 (65536 - 1000) / 256; // 高8位赋初值TL0 (65536 - 1000) % 256; // 低8位…...
记一次不太顺利的Docker重装
#记录工作 一、前言 默认情况下,Windows系统上Docker Desktop 安装在 :C:\Program Files\Docker\Docker; 目前正常下载能下载到最新版本是到v4.39.0,实际已经能找到v4.40.0版本来进行修复安装; 建议尽量不要改变Doc…...
【计网】TCP 协议详解 与 常见面试题
三次握手、四次挥手的常见面试题 不用死记,只需要清楚三次握手,四次挥手的流程,回答的时候心里要记住,假设网络是不可靠的 问题(1):为什么关闭连接时需要四次挥手,而建立连接却只要三次握手? 关…...
Docker介绍
Docker介绍 Docker 本身并不是容器,而是一个使用容器的工具。容器是 Linux 内核提供的技术,Docker 只是将这种技术的使用简便化了。Docker 的主要目标是 “Build,Ship and Run Any APP,Anywhere”(“一次封装,到处运行”…...
大模型推理--Qwen2.5-Omni在A100上的初体验
过去的一周Qwen2.5-Omni产生了很高的热度,吸引了很多人的目光。它的多模态确实很吸引人,放出来的demo体验还算尚可(语音对话的延迟还是太大),所以就在A100 PCIe上实地部署了一下,初步对其速度进行了测试&am…...
二分查找例题
本篇基于b站灵茶山艾府。 34. 在排序数组中查找元素的第一个和最后一个位置 给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。 如果数组中不存在目标值 target,返回 [-1, -1]。 你…...
新增一种线性回归的增量学习框架,已更新31个模型!Matlab回归预测大合集又更新啦!
目录 效果图基本介绍程序设计参考资料 效果图 基本介绍 一种线性回归的增量学习框架,程序研究的主要内容是线性回归模型的增量学习实现及其在房价预测中的应用,旨在通过分块处理数据逐步更新模型,以适应动态数据环境并减少计算资源消耗。 详…...
P1025 [NOIP 2001 提高组] 数的划分(DFS)
题目描述 将整数 n 分成 k 份,且每份不能为空,任意两个方案不相同(不考虑顺序)。 例如:n7,k3,下面三种分法被认为是相同的。 1,1,5; 1,5,1; 5,1,1. 问有多少种不同的分法。 输入格式 n,k …...
SQL Server存储过程和触发器的使用
存储过程 (1)创建存储过程,使用Employees表中的员工人数来初始化一个局部变量,并调用这个存储过程。 1. Create PROCEDURE test number1 int output --输出参数,可以从程序中返回信息 2. As 3. begin 4. D…...
Elastic 的 OpenTelemetry 分发版(EDOT)现已正式发布:开源、可用于生产环境的 OTel
作者:来自 Elastic Miguel Luna 及 Bahubali Shetti Elastic 自豪地宣布正式发布 Elastic OpenTelemetry 分发版(Elastic Distributions of OpenTelemetry - EDOT),其中包含 Elastic 自定义版本的 OpenTelemetry Collector 以及多…...
springMVC-Json交互处理
什么是JSON JSON(JavaScript Object Notation, JS 对象标记) 是一种轻量级的数据交换格式,目前使用特别广泛。 采用完全独立于编程语言的文本格式来存储和表示数据。 简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写,同时也…...
MySQL中的索引
explain关键字, MySQL索引特性 索引的概念 MySQL 索引是一种用于提高数据库查询效率的数据结构 数据库表中存储的数据都是以记录为单位的,如果在查询数据时直接一条条遍历表中的数据记录,那么查询的时间复杂度将会是 O ( N )。索引的价值在…...
AI小白:JavaPython开发环境双轨制搭建指南
文章目录 1 Python深度学习环境配置1.1 Anaconda生态体系建设1.2 JupyterLab高效工作流魔法命令与可视化调试扩展插件配置指南 2 Java深度学习方案:DL4J实战2.1 企业级部署架构设计2.2 集成传统Java系统Spring Boot微服务封装模型性能优化技巧 1 Python深度学习环境…...
《比特城的机密邮件:加密、签名与防篡改的守护之战》
点击下面图片带您领略全新的嵌入式学习路线 🔥爆款热榜 88万阅读 1.6万收藏 第一章:风暴前的密令 比特城的议会大厅内,首席长老艾德文握着一卷足有半人高的羊皮纸,眉头紧锁。纸上是即将颁布的《新纪元法典》——这份文件不仅内…...
Redis之布隆过滤器
面试场景切入 针对于电话号码问题的痛点 布隆过滤器是什么? 由一个初值都为0的bit数组和多个哈希函数构成,用来快速判断集合中是否存在某个元素。 设计思想 本质就是判断具体数据是否存在于一个大的集合中。布隆过滤器是一种类似Set的数据结构&#…...
这是一份简单优雅的Prompt Engineering教程
Prompt Engineering(提示工程)是通过精心设计输入文本(prompt)来引导大型语言模型(LLM)生成更准确、相关且符合预期的输出的技术。其核心在于通过调整提问的措辞、结构、上下文和附加信息,优化模…...
Java基础 4.6
1.成员方法练习 //编写类A:判断一个数是奇数还是偶数,返回boolean //根据行、列、字符打印对应行数和列数的字符,比如:行4 列4 字符# 则打印相应的效果 public class MethodExercise01 {public static void main(String[] args) …...
DApp实战篇:前端技术栈一览
前言 在前面一系列内容中,我们由浅入深地了解了DApp的组成,从本小节开始我将带领大家如何完成一个完整的DApp。 本小节则先从前端开始。 前端技术栈 在前端开发者速入:DApp中的前端要干些什么?文中我说过,即便是在…...
C++中如何比较两个字符串的大小--compare()函数实现
一、现在有一个问题描述:有两个字符串,要按照字典顺序比较它们的大小(注意所有的小写字母都大于所有的大写字母 )。 二、代码 #include <bits/stdc.h> using namespace std;int main() {string str1 "apple";…...
c++中的auto关键字
在 C 中,auto 是一个类型推断关键字(C11 引入),允许编译器根据变量的初始化表达式自动推导其类型。它极大地简化了代码编写,尤其在涉及复杂类型或模板的场景中。以下是 auto 的详细说明: 1. 基本用法 1.1 …...
zk源码—1.数据节点与Watcher机制及权限二
大纲 1.ZooKeeper的数据模型、节点类型与应用 (1)数据模型之树形结构 (2)节点类型与特性(持久 临时 顺序 ) (3)节点的状态结构(各种zxid 各种version) (4)节点的版本(version cversion aversion) (5)使用ZooKeeper实现锁(悲观锁 乐观锁) 2.发布订阅模式࿱…...
交换机和集线器的区别
集线器(Hub)—— 大喇叭广播站 工作原理: 集线器像村里的“大喇叭”,收到任何消息都会广播给所有人。 比如A对B说“你好”,全村人(C、D、E)都能听到,但只有B会回…...
微服务系统记录
记录下曾经工作涉及到微服务的相关知识。 1. 架构设计与服务划分 关键内容 领域驱动设计(DDD): 利用领域模型和限界上下文(Bounded Context)拆分业务,明确服务边界。通过事件风暴(Event Storm…...
同花顺客户端公司财报抓取分析
目标客户端下载地址:https://ft.51ifind.com/index.php?c=index&a=download PC版本 主要难点在登陆,获取token中的 jgbsessid (每次重新登录这个字段都会立即失效,且有效期应该是15天的) 抓取jgbsessid 主要通过安装mitmproxy 使用 mitmdump + 下边的脚本实现监听接口…...
二叉树与红黑树核心知识点及面试重点
二叉树与红黑树核心知识点及面试重点 一、二叉树 (Binary Tree) 1. 基础概念 定义:每个节点最多有两个子节点(左子节点和右子节点) 术语: 根节点:最顶层的节点 叶子节点:没有子节点的节点 深度…...
Rocket-JWT鉴权
目录 一、概述 二、相关依赖 三、环境准备 3.1 创建项目 3.2 读取私钥信息 3.3 token数据负载 3.4 生成token 四、Web鉴权 4.1 验证载体 4.2 接收请求 五、总结 Welcome to Code Blocks blog 本篇文章主要介绍了 [Rocket-JWT鉴权] ❤博主广交技术好友,喜…...
2025 年网络安全终极指南
我们生活在一个科技已成为日常生活不可分割的一部分的时代。对数字世界的依赖性日益增强的也带来了更大的网络风险。 网络安全并不是IT专家的专属特权,而是所有用户的共同责任。通过简单的行动,我们可以保护我们的数据、隐私和财务,降低成为…...
横扫SQL面试——PV、UV问题
📊 横扫SQL面试:UV/PV问题 🌟 什么是UV/PV? 在数据领域,UV(Unique Visitor,独立访客) 和 PV(Page View,页面访问量) 是最基础也最重要的指标&…...
ctf-show-杂项签到题
下载文件,解压需要密码,用010打开没看出什么 然后用Advanced Archive Password Recovery暴力破解,发现没用 怀疑是伪解密,解压出来发现加密受损用随波逐流修复加密文件 打开修复的加密文件直接得flag flag:flag{79d…...
对解释器模式的理解
对解释器模式的理解 一、场景1、题目【[来源](https://kamacoder.com/problempage.php?pid1096)】1.1 题目描述1.2 输入描述1.3 输出描述1.4 输入示例1.5 输出示例 二、不采用解释器模式1、代码2、“缺点” 三、采用解释器模式1、代码2、“优点” 四、思考1、解释器模式的意义…...
高频面试题(含笔试高频算法整理)基本总结回顾64
干货分享,感谢您的阅读! (暂存篇---后续会删除,完整版和持续更新见高频面试题基本总结回顾(含笔试高频算法整理)) 备注:引用请标注出处,同时存在的问题请在相关博客留言…...
python入门之从安装python及vscode开始
本篇将解决三个问题: 1. 如何下载及安装官方python? 2. 如何下载及安装vscode? 3. 如何配置vscode的python环境? 一、python下载及安装 1.搜索python,找到官网并打开 2.找到download,按需选择版本下载 …...
OpenGL学习笔记(模型材质、光照贴图)
目录 光照与材质光照贴图漫反射贴图采样镜面光贴图 GitHub主页:https://github.com/sdpyy OpenGL学习仓库:https://github.com/sdpyy1/CppLearn/tree/main/OpenGLtree/main/OpenGL):https://github.com/sdpyy1/CppLearn/tree/main/OpenGL 光照与材质 在现实世界里&…...
视觉_transform
visual_transform 图像分块 (Patch Embedding) 假设输入图像为 x ∈ R ∗ H ∗ ∗ W ∗ ∗ C ∗ x∈R^{*H**W**C*} x∈R∗H∗∗W∗∗C∗ C 是图像的通道数(例如,RGB图像的 C3) 将图像分割成N个大小为P*CP的patch,每个patch的大…...
Redis的安装及通用命令
二. Redis 的安装及通用命令 1. Ubuntu 安装 Redis (1) 切换到 root 用户: su root(2) 搜索 Redis 软件包 apt search redis(3) 安装 Redis apt install redis(4) 查看 Redis netstat -anp | grep redis(5) 切换到 Redis 目录下 cd /etc/redis/(6) 修改 Redis 配置文件:…...
Python 实现的运筹优化系统代码详解(0-1规划背包问题)
一、引言 在数学建模与实际决策场景的交织领域中,诸多复杂问题亟待高效且精准的解决方案。0-1 规划作为一种特殊且极为重要的优化方法,宛如一把万能钥匙,能够巧妙开启众多棘手问题的解决之门。它专注于处理决策变量仅能取 0 或 1 这两种极端状…...
护网蓝初面试题
《网安面试指南》https://mp.weixin.qq.com/s/RIVYDmxI9g_TgGrpbdDKtA?token1860256701&langzh_CN 5000篇网安资料库https://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247486065&idx2&snb30ade8200e842743339d428f414475e&chksmc0e4732df793fa3bf39…...
音视频学习(三十二):VP8和VP9
VP8 简介 全称:Video Processing 8发布者:原 On2 Technologies(2010 被 Google 收购)定位:开源视频压缩标准,主要竞争对手是 H.264应用: WebRTC 视频通信HTML5 <video> 标签(…...
美国mlb与韩国mlb的关系·棒球9号位
MLB(Major League Baseball,美国职业棒球大联盟)作为全球最高水平的职业棒球联赛,与韩国市场流行的“MLB”时尚品牌之间存在着授权合作关系,但两者在业务范畴和品牌定位上存在显著差异。 一、品牌授权背景:…...