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

C++ STL 多线程库用法介绍

目录

一:Atomic:

二:Thread

 1. 创建线程 

2. 小心移动(std::move)线程 

 3. 如何创建带参数的线程

4. 线程参数是引用类型时,要小心谨慎。

 5. 获取线程ID

6. jthread

7. 如何在线程中使用中断 stop_token

三:如何解决数据竞争

1.有问题的代码 

2.使用互斥 

3.预防死锁

4. 自动释放锁

5. 延迟锁

6. 共享锁

7. 线程安全的初始化

四:线程局部存储

五:线程通信

1.条件变量

2. 防止虚假唤醒

3. 防止唤醒丢失

4.信号量

5. std::latch

六:任务

 1. std::promise, std::future

2. 用std::promise, std::future进行线程同步

3. std::async

4. std::package_task


一:Atomic:

#include <atomic>
#include <thread>
#include <iostream>using namespace std;std::atomic_int x, y;
int r1, r2;
void writeX() {x.store(1);r1 = y.load();
}
void writeY() {y.store(1);r2 = x.load();
} int main() {for (int i = 0; i < 100; i++){x = 0;y = 0;std::thread a(writeX);std::thread b(writeY);a.join();b.join();std::cout << r1 << r2 << std::endl;}return 0;
}
//可能的输出有三种情况:01, 10, 11
//01:先执行线程a, 再执行线程b
//10:先执行线程b,再执行线程a
//11:执行线程a一半后调度到线程b,然后再回来  

二:Thread

 1. 创建线程 
#include <atomic>
#include <thread>
#include <iostream>using namespace std;void helloFunction() {cout << "function" << endl;
}class HelloFunctionObject {
public:void operator()() const {cout << "function object" << endl;}
};int main()
{thread t1(helloFunction); // functionHelloFunctionObject helloFunctionObject;thread t2(helloFunctionObject); // function objectthread t3([] { cout << "lambda function" << std::endl; }); // lambda functiont1.join(); //需要用join,否则可能会出现主线程退出时,t1线程还没有执行完的情况,引起异常t2.join();t3.join();return 0;
}
2. 小心移动(std::move)线程 
#include <atomic>
#include <thread>
#include <iostream>using namespace std;int main()
{std::thread t([] { cout << "lambda function"; });std::thread t2;t2 = std::move(t);std::thread t3([] { cout << "lambda function"; });/*此处代码有问题,当t2 已经获得线程t后,它已经是callable和joinable,再赋值t3会terminate*/ t2 = std::move(t3);  std::terminate
}
 3. 如何创建带参数的线程
#include <atomic>
#include <thread>
#include <iostream>using namespace std;//如何在线程中传递参数
void printStringCopy(string s) { cout << s; }
void printStringRef(const string& s) { cout << s; }int main()
{string s{ "C++" };thread tPerCopy([=] { cout << s; }); // C++thread tPerCopy2(printStringCopy, s); // C++tPerCopy.join();tPerCopy2.join();thread tPerReference([&] { cout << s; }); // C++thread tPerReference2(printStringRef, s); // C++tPerReference.join();tPerReference2.join(); 
}
4. 线程参数是引用类型时,要小心谨慎。
#include <iostream>using namespace std;using std::this_thread::sleep_for;
using std::this_thread::get_id;struct Sleeper {Sleeper(int& i_) :i{ i_ } {};void operator() (int k) {for (unsigned int j = 0; j <= 5; ++j) {sleep_for(std::chrono::milliseconds(100));i += k;}std::cout << get_id(); // undefined behaviour}
private:int& i;
};int main()
{int valSleeper = 1000;//valSleeper 作为引用类型传给线程,如果主线程先退出,t线程使用valSleeper会产生未定义行为, 并且主线程和t线程共享varSleeper,产生数据竞争,std::thread t(Sleeper(valSleeper), 5); t.detach();std::cout << valSleeper; // undefined behaviour
}
 5. 获取线程ID
using namespace std;
using std::this_thread::get_id;int main()
{std::cout << std::thread::hardware_concurrency() << std::endl; // 4std::thread t1([] { std::cout << get_id() << std::endl; }); // 139783038650112std::thread t2([] { std::cout << get_id() << std::endl; }); // 139783030257408std::cout << t1.get_id() << std::endl; // 139783038650112std::cout << t2.get_id() << std::endl; // 139783030257408t1.swap(t2);std::cout << t1.get_id() << std::endl; // 139783030257408std::cout << t2.get_id() << std::endl; // 139783038650112std::cout << get_id() << std::endl; // 140159896602432t1.join();t2.join();
}
6. jthread
#include <atomic>
#include <thread>
#include <iostream>using namespace std;
using std::this_thread::get_id;//jthread 自动join()的线程
int main()
{std::jthread thr{ [] { std::cout << "std::jthread" << "\n"; } }; // std::jthreadstd::cout << "thr.joinable(): " << thr.joinable() << "\n"; // thr.joinable(): true
}
7. 如何在线程中使用中断 stop_token
#include <atomic>
#include <thread>
#include <iostream>using namespace std;
using std::this_thread::get_id;
using namespace::std::literals;//字面量,比如0.2s, C++20能识别这种写法std::jthread nonInterruptable([] { // (1)  创建非中断线程int counter{ 0 };
while (counter < 10) {std::this_thread::sleep_for(0.2s);std::cerr << "nonInterruptable: " << counter << std::endl;++counter;
}});
std::jthread interruptable([](std::stop_token stoken) { // (2) 创建可中断线程int counter{ 0 };
while (counter < 10) {std::this_thread::sleep_for(0.2s);if (stoken.stop_requested()) return; // (3) 检查线程是否被中断std::cerr << "interruptable: " << counter << std::endl;++counter;
}});int main()
{std::this_thread::sleep_for(1s);std::cerr << "Main thread interrupts both jthreads" << std::endl;nonInterruptable.request_stop(); // (4)//请求中断,非中断线程不理会interruptable.request_stop();//请求中断,中断线程会响应
}

三:如何解决数据竞争

1.有问题的代码 
#include <atomic>
#include <thread>
#include <iostream>using namespace std;struct Worker {Worker(string n) :name(n) {};void operator() () {for (int i = 1; i <= 3; ++i) {this_thread::sleep_for(chrono::milliseconds(200));//流本身是线程安全的,但是cout是共享变量,它会独占流,多个线程访问cout时会引起数据竞争 cout << name << ": " << "Work " << i << endl;}}
private:string name;
};int main()
{thread herb = thread(Worker("Herb"));thread andrei = thread(Worker(" Andrei"));thread scott = thread(Worker(" Scott"));thread bjarne = thread(Worker(" Bjarne"));herb.join();andrei.join();scott.join();bjarne.join();}
2.使用互斥 
#include <atomic>
#include <thread>
#include <iostream>
#include <mutex>using namespace std;std::mutex mutexCout;struct Worker {Worker(string n) :name(n) {};void operator() () {for (int i = 1; i <= 3; ++i) {this_thread::sleep_for(chrono::milliseconds(200));mutexCout.lock();cout << name << ": " << "Work " << i << endl;mutexCout.unlock();}}
private:string name;
};int main()
{thread herb = thread(Worker("Herb"));thread andrei = thread(Worker("Andrei"));thread scott = thread(Worker("Scott"));thread bjarne = thread(Worker("Bjarne"));herb.join();andrei.join();scott.join();bjarne.join();}
3.预防死锁
m.lock();
sharedVar= getVar(); //如果此处抛出异常,会导致m.unlock未调用,锁不能被释放,其他线程无法得到锁,进而可能产生死锁
m.unlock()
#include <iostream>
#include <mutex>using namespace std;struct CriticalData {std::mutex mut;
};
void deadLock(CriticalData& a, CriticalData& b) {a.mut.lock();std::cout << "get the first mutex\n";std::this_thread::sleep_for(std::chrono::milliseconds(1));b.mut.lock();std::cout << "get the second mutex\n";a.mut.unlock(), b.mut.unlock();
}int main()
{CriticalData c1;CriticalData c2;//t1, t2在拿到锁后都在等对方释放锁std::thread t1([&] { deadLock(c1, c2); });std::thread t2([&] { deadLock(c2, c1); });t1.join();t2.join();
}
4. 自动释放锁
#include <atomic>
#include <thread>
#include <iostream>
#include <mutex>using namespace std;std::mutex mutexCout;
struct Worker {Worker(std::string n) :name(n) {};void operator() () {for (int i = 1; i <= 3; ++i) {std::this_thread::sleep_for(std::chrono::milliseconds(200));std::lock_guard<std::mutex> myLock(mutexCout);//自动释放锁std::cout << name << ": " << "Work " << i << std::endl;}}
private:std::string name;
};int main()
{thread herb = thread(Worker("Herb"));thread andrei = thread(Worker("Andrei"));thread scott = thread(Worker("Scott"));thread bjarne = thread(Worker("Bjarne"));herb.join();andrei.join();scott.join();bjarne.join();
}
5. 延迟锁
#include <atomic>
#include <thread>
#include <iostream>
#include <mutex>using namespace std;using namespace std;
struct CriticalData {mutex mut;
};
void deadLockResolved(CriticalData& a, CriticalData& b) {unique_lock<mutex>guard1(a.mut, defer_lock);cout << this_thread::get_id() << ": get the first lock" << endl;this_thread::sleep_for(chrono::milliseconds(1));unique_lock<mutex>guard2(b.mut, defer_lock);cout << this_thread::get_id() << ": get the second lock" << endl;cout << this_thread::get_id() << ": atomic locking" << endl;lock(guard1, guard2);
}int main()
{CriticalData c1;CriticalData c2;thread t1([&] { deadLockResolved(c1, c2); });thread t2([&] { deadLockResolved(c2, c1); });t1.join();t2.join();
}
6. 共享锁
#include <mutex>
...
std::shared_timed_mutex sharedMutex;
std::unique_lock<std::shared_timed_mutex> writerLock(sharedMutex);
std::shared_lock<std::shared_time_mutex> readerLock(sharedMutex);
std::shared_lock<std::shared_time_mutex> readerLock2(sharedMutex);
7. 线程安全的初始化
//常量表达式是线程安全的
struct MyDouble{
constexpr MyDouble(double v):val(v){};
constexpr double getValue(){ return val; }
private:
double val
};
constexpr MyDouble myDouble(10.5);
std::cout << myDouble.getValue(); // 10.5
//块内静态变量
void blockScope(){
static int MySharedDataInt= 2011;
}
//once_flag, call_once 
#include <mutex>
...
using namespace std;
once_flag onceFlag;
void do_once(){
call_once(onceFlag, []{ cout << "Only once." << endl; });
}
thread t1(do_once);
thread t2(do_once);

四:线程局部存储


std::mutex coutMutex;
thread_local std::string s("hello from ");
void addThreadLocal(std::string const& s2){
s+= s2;
std::lock_guard<std::mutex> guard(coutMutex);
std::cout << s << std::endl;
std::cout << "&s: " << &s << std::endl;
std::cout << std::endl;
}
std::thread t1(addThreadLocal, "t1");
std::thread t2(addThreadLocal, "t2");
std::thread t3(addThreadLocal, "t3");
std::thread t4(addThreadLocal, "t4");

五:线程通信

1.条件变量
#include <atomic>
#include <thread>
#include <iostream>
#include <mutex>
#include <condition_variable>using namespace std;std::mutex mutex_;
std::condition_variable condVar;
bool dataReady = false;
void doTheWork() {std::cout << "Processing shared data." << std::endl;
}
void waitingForWork() {std::cout << "Worker: Waiting for work." << std::endl;std::unique_lock<std::mutex> lck(mutex_);condVar.wait(lck, [] { return dataReady; });doTheWork();std::cout << "Work done." << std::endl;
}
void setDataReady() {std::lock_guard<std::mutex> lck(mutex_);dataReady = true;std::cout << "Sender: Data is ready." << std::endl;condVar.notify_one();
}int main()
{std::thread t1(waitingForWork);std::thread t2(setDataReady);t1.join();t2.join();
}
2. 防止虚假唤醒
//为了防止虚假唤醒,在唤醒前应进行条件检查,且发送方应将条件置为true。
//dataReady = true; //发送方设置条件满足
//[] { return dataReady; } //接收方进行条件检查
3. 防止唤醒丢失
//如果发送方在接收方等待之前,就发送了唤醒,可能会导致唤醒丢失,因此要做两件事:
//1: 要先等待,后发送唤醒
//2: 在接收方的等待函数中要检查是否满足条件 [] { return dataReady; };
4.信号量
#include <atomic>
#include <thread>
#include <iostream>
#include <mutex>
#include <condition_variable>
#include <semaphore>
#include <vector>using namespace std;std::vector<int> myVec;std::counting_semaphore<1> prepareSignal(0); // (1)
void prepareWork() {myVec.insert(myVec.end(), { 0, 1, 0, 3 });std::cout << "Sender: Data prepared." << '\n';prepareSignal.release(); // (2)
}void completeWork() {std::cout << "Waiter: Waiting for data." << '\n';prepareSignal.acquire(); // (3)myVec[2] = 2;std::cout << "Waiter: Complete the work." << '\n';for (auto i : myVec) std::cout << i << " ";std::cout << '\n';
}int main()
{std::thread t1(prepareWork);std::thread t2(completeWork);t1.join();t2.join();
}
5. std::latch
#include <atomic>
#include <thread>
#include <iostream>
#include <mutex>
#include <condition_variable>
#include <semaphore>
#include <vector>
#include <latch>using namespace std;std::mutex coutMutex;std::latch workDone(2);
std::latch goHome(1); // (5)
void synchronizedOut(const std::string s) {std::lock_guard<std::mutex> lo(coutMutex);std::cout << s;
}class Worker {
public:Worker(std::string n) : name(n) { };void operator() () {// notify the boss when work is donesynchronizedOut(name + ": " + "Work done!\n");workDone.count_down(); // (3) 完成工作// waiting before going homegoHome.wait();//等待老板发命令让他们回家synchronizedOut(name + ": " + "Good bye!\n");}
private:std::string name;
};int main()
{std::cout << "BOSS: START WORKING! " << '\n';Worker herb(" Herb"); // (1) 工人1std::thread herbWork(herb); //工人1必须完成自己的工作Worker scott(" Scott"); // (2) 工人2std::thread scottWork(scott);//工人2必须完成自己的工作workDone.wait(); // (4) 完成工作后等待std::cout << '\n';goHome.count_down();//老板发命令回家std::cout << "BOSS: GO HOME!" << '\n';herbWork.join();scottWork.join();
}

6. std::barrier

#include <barrier>
#include <iostream>
#include <string>
#include <syncstream>
#include <thread>
#include <vector>int main()
{const auto workers = { "Anil", "Busara", "Carl" };auto on_completion = []() noexcept{// locking not needed herestatic auto phase ="... done\n""Cleaning up...\n";std::cout << phase;phase = "... done\n";};std::barrier sync_point(std::ssize(workers), on_completion);auto work = [&](std::string name){std::string product = "  " + name + " worked\n";std::osyncstream(std::cout) << product;  // ok, op<< call is atomicsync_point.arrive_and_wait();product = "  " + name + " cleaned\n";std::osyncstream(std::cout) << product;sync_point.arrive_and_wait();};std::cout << "Starting...\n";std::vector<std::jthread> threads;threads.reserve(std::size(workers));for (auto const& worker : workers)threads.emplace_back(work, worker);
}

六:任务

 1. std::promise, std::future
#include <future>
#include <iostream>void product(std::promise<int>&& intPromise, int a, int b) {intPromise.set_value(a * b);
}
int main()
{int a = 20;int b = 10;std::promise<int> prodPromise;std::future<int> prodResult = prodPromise.get_future();std::jthread prodThread(product, std::move(prodPromise), a, b);std::cout << "20*10= " << prodResult.get(); // 20*10= 200
}
2. 用std::promise, std::future进行线程同步
#include <future>
#include <iostream>void doTheWork() {std::cout << "Processing shared data." << std::endl;
}
void waitingForWork(std::future<void>&& fut) {std::cout << "Worker: Waiting for work." <<std::endl;fut.wait();doTheWork();std::cout << "Work done." << std::endl;
}
void setDataReady(std::promise<void>&& prom) {std::cout << "Sender: Data is ready." <<std::endl;prom.set_value();
}int main()
{std::promise<void> sendReady;auto fut = sendReady.get_future();std::jthread t1(waitingForWork, std::move(fut));std::jthread t2(setDataReady, std::move(sendReady));}
3. std::async
#include <future>
#include <iostream>using std::chrono::duration;
using std::chrono::system_clock;
using std::launch;int main()
{auto begin = system_clock::now();auto asyncLazy = std::async(launch::deferred, [] { return system_clock::now(); });auto asyncEager = std::async(launch::async, [] { return system_clock::now(); });std::this_thread::sleep_for(std::chrono::seconds(1));auto lazyStart = asyncLazy.get() - begin;auto eagerStart = asyncEager.get() - begin;auto lazyDuration = duration<double>(lazyStart).count();auto eagerDuration = duration<double>(eagerStart).count();std::cout << lazyDuration << " sec"; // 1.00018 sec.std::cout << eagerDuration << " sec"; // 0.00015489 sec.
}
#include <future>
#include <iostream>
#include <thread>using std::chrono::duration;
using std::chrono::system_clock;
using std::launch;int main()
{int res;std::thread t([&] { res = 2000 + 11; });t.join();std::cout << res << std::endl; // 2011auto fut = std::async([] { return 2000 + 11; });//异步调用std::cout << fut.get() << std::endl; // 2011
}
4. std::package_task
#include <future>
#include <iostream>
#include <queue>
#include <thread>using namespace std;
using std::chrono::duration;
using std::chrono::system_clock;
using std::launch;struct SumUp {int operator()(int beg, int end) {for (int i = beg; i < end; ++i) sum += i;return sum;}
private:int beg;int end;int sum{ 0 };
};int main()
{SumUp sumUp1, sumUp2;packaged_task<int(int, int)> sumTask1(sumUp1);//任务1packaged_task<int(int, int)> sumTask2(sumUp2);//任务2future<int> sum1 = sumTask1.get_future(); //任务1的结果future<int> sum2 = sumTask2.get_future(); //任务2的结果deque< packaged_task<int(int, int)>> allTasks; //存储所有的任务allTasks.push_back(move(sumTask1));//将任务1加入队列allTasks.push_back(move(sumTask2));//将任务2加入队列int begin{ 1 };int increment{ 5000 };int end = begin + increment;while (not allTasks.empty()) {packaged_task<int(int, int)> myTask = move(allTasks.front());//取出1个任务allTasks.pop_front();thread sumThread(move(myTask), begin, end);//执行这个任务begin = end;end += increment;sumThread.detach();}auto sum = sum1.get() + sum2.get();//查询任务的结果cout << sum;}

相关文章:

C++ STL 多线程库用法介绍

目录 一&#xff1a;Atomic&#xff1a; 二&#xff1a;Thread 1. 创建线程 2. 小心移动(std::move)线程 3. 如何创建带参数的线程 4. 线程参数是引用类型时&#xff0c;要小心谨慎。 5. 获取线程ID 6. jthread 7. 如何在线程中使用中断 stop_token 三&#xff1a;如何…...

Jmeter实现接口自动化

自动化测试理论知识 什么是自动化测试&#xff1f; 让程序或工具代替人为执行测试用例什么样的项目适合做自动化&#xff1f; 1、项目周期长 --多长算长&#xff1f;&#xff08;自己公司运营项目&#xff09; 2、需求稳定&#xff08;更多具体功能/模块&#xff09; 3、需要…...

【大模型】多模型在大模型中的调度艺术:解锁效率与协同的新境界

多模型在大模型中的调度艺术&#xff1a;解锁效率与协同的新境界 引言一、多模型与大模型的概念解析二、多模型调度的必要性三、多模型调度的关键技术3.1 负载均衡与动态分配3.2 模型间通信与协作3.3 模型选择与优化 四、多模型运行优化策略4.1 异构计算平台的利用4.2 模型压缩…...

LeetCode 704, 290, 200

目录 704. 二分查找题目链接标签思路代码 290. 单词规律题目链接标签思路代码 200. 岛屿数量题目链接标签思路代码 704. 二分查找 题目链接 704. 二分查找 标签 数组 二分查找 思路 这道题是 二分查找 最经典的一道题&#xff0c;掌握了本题的思想就进入了 二分 思想的大…...

如何利用Java进行大数据处理?

如何利用Java进行大数据处理&#xff1f; 大家好&#xff0c;我是微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01; 1. 引言 在当今信息爆炸的时代&#xff0c;处理大数据是许多应用程序和系统的核心需求之一。Java作为一种…...

【论文通读】GUICourse: From General Vision Language Model to Versatile GUI Agent

GUICourse: From General Vision Language Model to Versatile GUI Agent 前言AbstractMotivationSolutionGUICourseGUIEnvGUIEnv-globalGUIEnv-local GUIActGUIAct (web-single)GUIAct (web-multi)GUIAct (smartphone) GUIChat ExperimentsMain ResultAblation Study Conclusi…...

王道考研数据机构:中缀表达式转为后缀表达式

实现方法&#xff1a; 初始化一个栈&#xff0c;用于保存暂时还不能确定运算顺序的运算符。从左到右处理各个元素&#xff0c;直到末尾。可能遇到三种情况: 遇到操作数。直接加入后缀表达式遇到界限符。遇到“(”直接入栈;遇到“)”则依次弹出栈内运算符并加入后缀表达式&…...

PL/SQL安装+汉化教程

PL/SQL安装教程 一、安装&#xff1a; 登陆官网&#xff1a;PL/SQL Developer - Allround Automations下载 下载PL/SQL稳定版本12.0.7 根据自己计算机版本安装相适配的版本。我这里安装X64-bit版本 进行安装&#xff1a; 根据情况去更改安装&#xff0c;我这里全部下一步…...

Qt | Qt 线程相关类概述和举例

Qt 是一个广泛用于跨平台应用开发的框架。在 Qt 中,多线程支持是其核心特性之一,它允许开发者在不同平台上创建并发应用。以下是 Qt 中与线程相关的类概述及其使用示例。 Qt 中的线程相关类 QThread QThread 是 Qt 中用于创建和管理线程的基类。通过派生并重写 run() 函数…...

Linux 复现Docker NAT网络

Linux 复现Docker NAT网络 docker 网络的构成分为宿主机docker0网桥和为容器创建的veth 对构成。这个默认网络命名空间就是我们登陆后日常使用的命名空间 使用ifconfig命令查看到的就是默认网络命名空间&#xff0c;docker0就是网桥&#xff0c;容器会把docker0当成路由&…...

HBuilder X 小白日记03-用css制作简单的交互动画

:hover选择器&#xff0c;用于选择鼠标指针浮动在上面的元素。 :hover选择器可用于所有元素&#xff0c;不只是链接 :link选择器 设置指向未被访问页面的链接的样式 :visited选择器 用于设置指向已被访问的页面的链接 :active选择器 用于活动链接...

【深度学习练习】心脏病预测

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 一、什么是RNN RNN与传统神经网络最大的区别在于&#xff0c;每次都会将前一次的输出结果&#xff0c;带到下一隐藏层中一起训练。如下图所示&#xff1a; …...

创建react的脚手架

Create React App 中文文档 (bootcss.com) 网址&#xff1a;creat-react-app.bootcss.com 主流的脚手架&#xff1a;creat-react-app 创建脚手架的方法&#xff1a; 方法一&#xff08;JS默认&#xff09;&#xff1a; 1. npx create-react-app my-app 2. cd my-app 3. …...

用例导图CMind

突然有一些觉悟&#xff0c;程序猿不能只会吭哧吭哧的低头做事&#xff0c;应该学会怎么去展示自己&#xff0c;怎么去宣传自己&#xff0c;怎么把自己想做的事表述清楚。 于是&#xff0c;这两天一直在整理自己的作品&#xff0c;也为接下来的找工作多做点准备。接下来…...

C++ 仿函数

一、介绍 CSTL中的仿函数&#xff0c;又被称为函数对象&#xff0c;其实就是&#xff1a;重载了()运算符的类。 因为在使用重载的operator()时&#xff0c;类似于函数调用&#xff0c;因此被称为仿函数。 ※注意※&#xff1a;仿函数本质上是一个类&#xff0c;不是函数。 二…...

Redhat 安装 docker 网络连接超时问题

目录 添加阿里云的Docker CE仓库 更新YUM缓存 安装 Docker Engine 启动并设置Docker自启动 验证 Docker 安装 [userlocalhost ~]$ sudo yum-config-manager --add-repohttps://download.docker.com/linux/centos/docker-ce.repo 正在更新 Subscription Management 软件仓库…...

Java面试题:undo log和redo log

undo log和redo log的区别 缓冲池(buffer pool): 主内存中的一个区域,可以缓存磁盘上经常被操作的数据,在执行crud时先操作缓冲池的数据以减少磁盘io 数据页(page): InnoDB存储引擎管理的最小单元,每页大小为16kb,页中存储的是行数据 redo log 重做日志,用来实现任务的持…...

【Scrapy】Scrapy 中间件等级设置规则

准我快乐地重饰演某段美丽故事主人 饰演你旧年共寻梦的恋人 再去做没流着情泪的伊人 假装再有从前演过的戏份 重饰演某段美丽故事主人 饰演你旧年共寻梦的恋人 你纵是未明白仍夜深一人 穿起你那无言毛衣当跟你接近 &#x1f3b5; 陈慧娴《傻女》 Scrapy 是…...

SDK环境的安装(测试使用)

1、安装 将文件解压至目录,我的目录为:D:\Program Files\Android 解压后如下: 下载链接如下: sdk下载 提取码见文章最后: 2、配置环境 1、在环境变量中,选择系统变量,点击新建。 变量名:ANDROID_HOME 变量值:“你自己的android-sdk安装路径” (例如我的:D:\Pro…...

【matlab】【python】爬虫实战

目录 引言 具体步骤 1.设置请求选项 2.发送请求并获取响应 3.设置正则表达式 4.执行正则表达式匹配 matlab完整代码 python代码示例 引言 在当今这个信息爆炸的时代&#xff0c;数据已成为推动社会进步和企业发展的核心动力之一。随着互联网的普及和技术的飞速发展&am…...

Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)

目录 1.TCP的连接管理机制&#xff08;1&#xff09;三次握手①握手过程②对握手过程的理解 &#xff08;2&#xff09;四次挥手&#xff08;3&#xff09;握手和挥手的触发&#xff08;4&#xff09;状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...

C# SqlSugar:依赖注入与仓储模式实践

C# SqlSugar&#xff1a;依赖注入与仓储模式实践 在 C# 的应用开发中&#xff0c;数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护&#xff0c;许多开发者会选择成熟的 ORM&#xff08;对象关系映射&#xff09;框架&#xff0c;SqlSugar 就是其中备受…...

用docker来安装部署freeswitch记录

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

DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”

目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...

NPOI Excel用OLE对象的形式插入文件附件以及插入图片

static void Main(string[] args) {XlsWithObjData();Console.WriteLine("输出完成"); }static void XlsWithObjData() {// 创建工作簿和单元格,只有HSSFWorkbook,XSSFWorkbook不可以HSSFWorkbook workbook new HSSFWorkbook();HSSFSheet sheet (HSSFSheet)workboo…...

【前端异常】JavaScript错误处理:分析 Uncaught (in promise) error

在前端开发中&#xff0c;JavaScript 异常是不可避免的。随着现代前端应用越来越多地使用异步操作&#xff08;如 Promise、async/await 等&#xff09;&#xff0c;开发者常常会遇到 Uncaught (in promise) error 错误。这个错误是由于未正确处理 Promise 的拒绝&#xff08;r…...

DeepSeek源码深度解析 × 华为仓颉语言编程精粹——从MoE架构到全场景开发生态

前言 在人工智能技术飞速发展的今天&#xff0c;深度学习与大模型技术已成为推动行业变革的核心驱动力&#xff0c;而高效、灵活的开发工具与编程语言则为技术创新提供了重要支撑。本书以两大前沿技术领域为核心&#xff0c;系统性地呈现了两部深度技术著作的精华&#xff1a;…...

Vue 3 + WebSocket 实战:公司通知实时推送功能详解

&#x1f4e2; Vue 3 WebSocket 实战&#xff1a;公司通知实时推送功能详解 &#x1f4cc; 收藏 点赞 关注&#xff0c;项目中要用到推送功能时就不怕找不到了&#xff01; 实时通知是企业系统中常见的功能&#xff0c;比如&#xff1a;管理员发布通知后&#xff0c;所有用户…...

高抗扰度汽车光耦合器的特性

晶台光电推出的125℃光耦合器系列产品&#xff08;包括KL357NU、KL3H7U和KL817U&#xff09;&#xff0c;专为高温环境下的汽车应用设计&#xff0c;具备以下核心优势和技术特点&#xff1a; 一、技术特性分析 高温稳定性 采用先进的LED技术和优化的IC设计&#xff0c;确保在…...

Ray框架:分布式AI训练与调参实践

Ray框架&#xff1a;分布式AI训练与调参实践 系统化学习人工智能网站&#xff08;收藏&#xff09;&#xff1a;https://www.captainbed.cn/flu 文章目录 Ray框架&#xff1a;分布式AI训练与调参实践摘要引言框架架构解析1. 核心组件设计2. 关键技术实现2.1 动态资源调度2.2 …...