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;}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1474484.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

leetcode每日一题-3033. 修改矩阵

题目描述&#xff1a; 解题思路&#xff1a;简单题目&#xff0c;思路非常直接。对列进行遍历&#xff0c;记录下最大值&#xff0c;然后再遍历一遍&#xff0c;把-1替换为最大值。需要注意的是进行列遍历和行遍历是不同的。 官方题解&#xff1a; class Solution { public:v…

图片管理新纪元:高效批量横向拼接图片,一键生成灰色艺术效果,打造专业视觉体验!

在数字时代&#xff0c;图片已成为我们生活和工作中不可或缺的一部分。但面对海量的图片&#xff0c;如何高效地进行批量管理、拼接和调色&#xff0c;成为许多人面临的难题。今天&#xff0c;我们为您带来了一款颠覆性的图片管理工具&#xff0c;让您轻松实现图片批量横向拼接…

STM32快速复习(七)IIC通信

文章目录 前言一、IIC是什么&#xff1f;二、标准库函数二、标准库示例代码总结 前言 IIC通信算是我在大学和面试中用的最多&#xff0c;问的最多的通信协议 工作后也经常用到&#xff0c;只是我负责的工作内容用的少&#xff0c;但是&#xff0c;一般项目中使用也是非常多的一…

Redis 五大数据类型底层原理

0、前言 本文涉及的主题&#xff1a; redis 对象存储 底层数据结构&#xff1a;int、embstr、raw、ziplist、listpack、quicklist、skiplist、intset、hashtable redis 数据类型&#xff1a;string、list、set、zset、hash 1、对象存储、底层编码、数据类型 1.1 对象存储…

linux RTC时钟时间出现了明显的偏移

RTC时钟时间出现了明显的偏移 1、开发环境2、问题阐述3、验证问题3.1、首先去排查了硬件电路和芯片电压不稳定的问题。3.2、晶振的问题。3.3、芯片本身3.4、芯片寄存器 4、代码修改 1、开发环境 平台&#xff1a;imx6ul kernel版本&#xff1a;linux4.1.5 RTC芯片&#xff1a;…

xxl-job集成SpringBoot

安装xxl-job客户端一般有很多方式&#xff0c;我这里给大家提供两种安装方式&#xff0c;包含里面的各项配置等等。 前期需要准备好MySQL数据库。复制SQL到数据库里面。 # # XXL-JOB v2.4.2-SNAPSHOT # Copyright (c) 2015-present, xuxueli.CREATE database if NOT EXISTS x…

001uboot体验

1.uboot的作用&#xff1a; 上电->uboot启动->关闭看门狗、初始化时钟、sdram、uart等外设->把内核文件从flash读取到SDRAM->引导内核启动->挂载根文件系统->启动根文件系统的应用程序 2.uboot编译 uboot是一个通用的裸机程序&#xff0c;为了适应各种芯片&…

Redis常用命令——Set、Zset篇

文章目录 一、Set相关命令操作 SADD SMEMBERS SISMEMBER SCARD SPOP SMOVE SREM SINTER 与 SINTERSTORE SUNION 与 SUNIONSTORE SDIFF 与 SDIFFSTORE Set命令小结 二、Zset 相关命令操作 ZADD ZCARD ZCOUNT ZRANGE ZREVRANGE ZPOPMAX BZPOPMAX ZPOPMIN 与 BZPOPMIN ZRANK 与 …

【刷题汇总--字符串中找出连续最长的数字串、岛屿数量、拼三角】

C日常刷题积累 今日刷题汇总 - day0071、字符串中找出连续最长的数字串1.1、题目1.2、思路1.3、程序实现 -- 比较1.4、程序实现 -- 双指针 2、岛屿数量2.1、题目2.2、思路2.3、程序实现 - dfs 3、拼三角3.1、题目3.2、思路3.3、程序实现 -- 蛮力法3.4、程序实现 -- 巧解(单调性…

Matlab协方差矩阵分解法生成随机场

Matlab协方差矩阵分解法生成随机场 相关系数矩阵 % function outcohesion(x,y,mu,theta) % end % xyload(F:\Research-OUC\基于机器许学习模型的海底斜坡可靠度研究\基于comsol的斜坡稳定性分析\comsol网格操作\grid_operate-matlab.mphtxt); % xxy(:,1); % yxy(:,2); Xlinspac…

贪心 | Java | LeetCode 455, 376, 53 做题总结

贪心算法介绍 贪心算法&#xff1a;贪心的本质是选择每一阶段的局部最优&#xff0c;从而达到全局最优。 说实话贪心算法并没有固定的套路。 一般解题步骤 贪心算法一般分为如下四步&#xff1a; ① 将问题分解为若干个子问题 ② 找出适合的贪心策略 ③ 求解每一个子问题的…

从打印到监测:揭秘3D生物打印中天然水凝胶的创新之路?

从打印到监测&#xff1a;揭秘3D生物打印中天然水凝胶的创新之路&#xff1f; 在组织工程和再生医学领域&#xff0c;生物墨水是构建3D组织结构的关键材料。传统上&#xff0c;生物墨水主要基于细胞外基质 (ECM) 水凝胶&#xff0c;例如胶原蛋白、明胶、脱乙酰壳多糖、海藻酸盐…

Matplotlib 学习

知识点 1.plot()&#xff1a;用于绘制线图和 散点图scatter() 函数&#xff1a;plot() 函数可以接受许多可选参数&#xff0c;用于控制图形的外观&#xff0c;例如&#xff1a;颜色: colorblue 控制线条的颜色。线型: linestyle-- 控制线条的样式&#xff0c;例如虚线。标记…

ssrf结合redis未授权getshell

目录 漏洞介绍 SSRF Redis未授权 利用原理 环境搭建 利用过程 rockylinux cron计划任务反弹shell 写公钥免密登录 ubuntu 写公钥免密登录 漏洞介绍 SSRF SSRF&#xff08;server side request forgrey&#xff09;服务端请求伪造&#xff0c;因后端未过滤用户输入&…

昇思25天学习打卡营第19天|LSTM+CRF序列标注

概述 序列标注指给定输入序列&#xff0c;给序列中每个Token进行标注标签的过程。序列标注问题通常用于从文本中进行信息抽取&#xff0c;包括分词(Word Segmentation)、词性标注(Position Tagging)、命名实体识别(Named Entity Recognition, NER)等。 条件随机场&#xff08…

【OJ】运行时错误(Runtime Error)导致递归爆栈问题

在进行OJ赛时&#xff0c; 题目&#xff1a;给你一个整数n&#xff0c;问最多能将其分解为多少质数的和。在第一行输出最多的质数数量k,下一行输出k个整数&#xff0c;为这些质数。 出现运行时错误 代码如下&#xff1a; def main():# code heren int(eval(input()))list …

Vatee万腾平台:创新科技,驱动未来

在科技日新月异的今天&#xff0c;每一个创新的火花都可能成为推动社会进步的重要力量。Vatee万腾平台&#xff0c;作为科技创新领域的佼佼者&#xff0c;正以其卓越的技术实力、前瞻性的战略眼光和不懈的探索精神&#xff0c;驱动着未来的车轮滚滚向前。 Vatee万腾平台深知&am…

flask使用定时任务flask_apscheduler(APScheduler)

Flask-APScheduler描述: Flask-APScheduler 是一个 Flask 扩展&#xff0c;增加了对 APScheduler 的支持。 APScheduler 有三个内置的调度系统可供您使用&#xff1a; Cron 式调度&#xff08;可选开始/结束时间&#xff09; 基于间隔的执行&#xff08;以偶数间隔运行作业…

Linux系统安装软件包的方法rpm和yum详解

起因&#xff1a; 本篇文章是记录学习Centos7的历程 关于rpm 常见命令 1&#xff09;查看已经安装的软件包 rpm -q 软件包名 2&#xff09;查看文件的相关信息 rpm -qi 软件包名 3&#xff09;查看软件包的依赖关系 就是说要想安装这个软件包&#xff0c;就必须把一些前…

【matlab】状态空间模型与传递函数模型的建立与转换

目录 SISO系统 MIMO系统 状态空间模型 状态空间模型到传递函数模型的转换 传递函数模型到状态空间模型的转换 (1) 转换函数ss() (2) 规范形转换函数canon() (3) 常微分方程(传递函数)转换为状态空间模型函数dif2ss() 状态空间模型的变换 特征值、特征向量与广义特征向量的计算…