C++11 多线程学习
C++11学习
一、多线程
1、模板线程是以右值传递的
template <class Fn, class... Args> explicit thread(Fn&& fn, Args&&... args)
则需要使用到std::ref
和std::cref
很好地解决了这个问题,std::ref
可以包装按引用传递的值。
std::cref
可以包装按const引用传递的值。
// Compiler: MSVC 19.29.30038.1
// C++ Standard: C++17
#include <iostream>
#include <thread>
using namespace std;
template<class T> void changevalue(T &x, T val) {x = val;
}
int main() {thread th[100];int nums[100];for (int i = 0; i < 100; i++)th[i] = thread(changevalue<int>, ref(nums[i]), i+1);for (int i = 0; i < 100; i++) {th[i].join();cout << nums[i] << endl;}return 0;
}
2、线程一些注意事项
线程是在thread对象被定义的时候开始执行的,而不是在调用join函数时才执行的,调用join函数只是阻塞等待线程结束并回收资源。
分离的线程(执行过detach的线程)会在调用它的线程结束或自己结束时释放资源。
线程会在函数运行完毕后自动释放,不推荐利用其他方法强制结束线程,可能会因资源未释放而导致内存泄漏。
没有执行join或detach的线程在程序结束时会引发异常
3、std::mutex互斥锁
三个方法
lock 上锁
unlock 解锁
bool try_lock() 尝试将mutex上锁。如果mutex未被上锁,则将其上锁并返回true;如果mutex已被锁则返回false。
4、std::atomic & atomic_flag
意思是该类型的变量是原子类型的操作。因为mutex的资源浪费较大,总是要上锁解锁的。引入atomic
std::atomic_int`只是`std::atomic<int>
atomic_int n = 0; 表示定义一个int类型的原子属性的变量n,他是线程安全的
atomic() noexcept = default | 默认构造函数 | 构造一个atomic对象(未初始化,可通过atomic_init进行初始化) |
---|---|---|
constexpr atomic(T val) noexcept | 初始化构造函数 | 构造一个atomic对象,用val 的值来初始化 |
atomic_flag,原子布尔类型,不同于 std::atomic , std::atomic_flag
不提供加载或存储操作。
成员函数
(构造函数) | 构造 atomic_flag (公开成员函数) |
---|---|
operator= | 赋值运算符 (公开成员函数) |
clear | 原子地设置标志为 false (公开成员函数) |
test_and_set | 原子地设置标志为 true 并获得其先前值 (公开成员函数) |
#include <thread>
#include <vector>
#include <iostream>
#include <atomic>//ATOMIC_FLAG_INIT 置为false
std::atomic_flag lock = ATOMIC_FLAG_INIT;void f(int n)
{for (int cnt = 0; cnt < 100; ++cnt) {//std::memory_order_acquire 内存访问while (lock.test_and_set(std::memory_order_acquire)) // 获得锁; // 自旋std::cout << "Output from thread " << n << '\n';lock.clear(std::memory_order_release); // 释放锁}
}int main()
{std::vector<std::thread> v;for (int n = 0; n < 10; ++n) {v.emplace_back(f, n);}for (auto& t : v) {t.join();}
}
5、std::async&std::future&std::shared_future
std::async定义在future
头文件中,新的线程类,执行效率更高于thread,并且可以选择同步异步执行,还有返回值可以选择调用时间。
重载版本 | 作用 |
---|---|
template <class Fn, class… Args> future<typename result_of<Fn(Args…)>::type> async (Fn&& fn, Args&&… args) | 异步或同步(根据操作系统而定)以args为参数执行fn 同样地,传递引用参数需要 std::ref 或std::cref |
template <class Fn, class… Args> future<typename result_of<Fn(Args…)>::type> async (launch policy, Fn&& fn, Args&&… args); | 异步或同步(根据policy 参数而定(见下文))以args为参数执行fn,引用参数同上。std::launch有2个枚举值和1个特殊值:枚举值:launch::async、launch::deferred、launch::async |
// Compiler: MSVC 19.29.30038.1
// C++ Standard: C++17
#include <iostream>
#include <thread>
#include <future>
using namespace std;
int main() {async(launch::async, [](const char *message){cout << message << flush;}, "Hello, ");cout << "World!" << endl;return 0;
}
其async的返回值std::future,是一个模板对象。
其成员函数有
一般:T get() 当类型为引用:R& future<R&>::get() 当类型为void:void future::get() | 阻塞等待线程结束并获取返回值。 若类型为void,则与 future::wait() 相同。只能调用一次。 |
---|---|
void wait() const | 阻塞等待线程结束 |
template <class Rep, class Period> future_status wait_for(const chrono::duration<Rep,Period>& rel_time) const; | 阻塞等待rel_time(rel_time是一段时间), 若在这段时间内线程结束则返回future_status::ready 若没结束则返回future_status::timeout 若async是以launch::deferred启动的,则不会阻塞并立即返回future_status::deferred |
std::future的作用并不只有获取返回值,它还可以检测线程是否已结束、阻塞等待,所以对于返回值是void的线程来说,future也同样重要。
// Compiler: MSVC 19.29.30038.1
// C++ Standard: C++17
#include <iostream>
#include <future>
using namespace std;
void count_big_number() {// C++14标准中,可以在数字中间加上单// 引号 ' 来分隔数字,使其可读性更强for (int i = 0; i <= 10'0000'0000; i++);
}
int main() {future<void> fut = async(launch::async, count_big_number);cout << "Please wait" << flush;// 每次等待1秒while (fut.wait_for(chrono::seconds(1)) != future_status::ready)cout << '.' << flush;cout << endl << "Finished!" << endl;return 0;
}
std::shared_future类模板 std::shared_future
提供访问异步操作结果的机制,类似 std::future ,除了允许多个线程等候同一共享状态。不同于仅可移动的 std::future (故只有一个实例能指代任何特定的异步结果),std::shared_future
可复制而且多个 shared_future 对象能指代同一共享状态。
若每个线程通过其自身的 shared_future
对象副本访问,则从多个线程访问同一共享状态是安全的。
#include <iostream>
#include <future>
#include <chrono>int main()
{ std::promise<void> ready_promise, t1_ready_promise, t2_ready_promise;std::shared_future<void> ready_future(ready_promise.get_future());std::chrono::time_point<std::chrono::high_resolution_clock> start;auto fun1 = [&, ready_future]() -> std::chrono::duration<double, std::milli> {t1_ready_promise.set_value();ready_future.wait(); // 等待来自 main() 的信号return std::chrono::high_resolution_clock::now() - start;};auto fun2 = [&, ready_future]() -> std::chrono::duration<double, std::milli> {t2_ready_promise.set_value();ready_future.wait(); // 等待来自 main() 的信号return std::chrono::high_resolution_clock::now() - start;};auto result1 = std::async(std::launch::async, fun1);auto result2 = std::async(std::launch::async, fun2);// 等待线程变为就绪t1_ready_promise.get_future().wait();t2_ready_promise.get_future().wait();// 线程已就绪,开始时钟start = std::chrono::high_resolution_clock::now();// 向线程发信使之运行ready_promise.set_value();std::cout << "Thread 1 received the signal "<< result1.get().count() << " ms after start\n"<< "Thread 2 received the signal "<< result2.get().count() << " ms after start\n";
}
6、std::promise
是实际上是std::future的一个包装,但是作用确实想实现线程函数的左值引用,将函数执行结果带回来。
一般: void set_value (const T& val) void set_value (T&& val) 当类型为引用:void promise<R&>::set_value (R& val) 当类型为void:void promise::set_value (void) | 设置promise的值并将共享状态设为ready(将future_status设为ready) void特化:只将共享状态设为ready |
---|---|
future get_future() | 构造一个future对象,其值与promise相同,status也与promise相同 |
// Compiler: MSVC 19.29.30038.1
// C++ Standard: C++17
#include <iostream>
#include <thread>
#include <future> // std::promise std::future
using namespace std;template<class ... Args> decltype(auto) sum(Args&&... args) {return (0 + ... + args);
}template<class ... Args> void sum_thread(promise<long long> &val, Args&&... args) {val.set_value(sum(args...)); //设置值
}int main() {promise<long long> sum_value;//目的还是传递左值引用thread get_sum(sum_thread<int, int, int>, ref(sum_value), 1, 10, 100);cout << sum_value.get_future().get() << endl; //获取值get_sum.join(); // 感谢评论区 未来想做游戏 的提醒return 0;
}
6、std::this_thread中的一些静态方法
std::thread::id get_id() noexcept | 获取当前线程id |
---|---|
template<class Rep, class Period> void sleep_for( const std::chrono::duration<Rep, Period>& sleep_duration ) | 等待sleep_duration (sleep_duration 是一段时间) |
void yield() noexcept | 暂时放弃线程的执行,将主动权交给其他线程 (放心,主动权还会回来) |
template <class Clock, class Duration> void sleep_until (const chrono::time_point<Clock,Duration>& abs_time); | 等到到abs_time这个时间到来再执行 |
#include <iostream>
#include <thread>
#include <atomic>
using namespace std;
atomic_bool ready = 0; //原子类型变量 bool类型 atomic<bool>
// uintmax_t ==> unsigned long long
void sleep(uintmax_t ms) {this_thread::sleep_for(chrono::milliseconds(ms));
}
void count() {while (!ready) this_thread::yield();//暂时交出cpu执行,for (int i = 0; i <= 20'0000'0000; i++);cout << "Thread " << this_thread::get_id() << " finished!" << endl;return;
}
int main() {thread th[10];for (int i = 0; i < 10; i++)th[i] = thread(::count);sleep(5000);ready = true;cout << "Start!" << endl;for (int i = 0; i < 10; i++)th[i].join();return 0;
}
// this_thread::sleep_for example
#include <iostream> // std::cout
#include <iomanip> // std::put_time
#include <thread> // std::this_thread::sleep_until
#include <chrono> // std::chrono::system_clock
#include <ctime> // std::time_t, std::tm, std::localtime, std::mktime
int main()
{
using std::chrono::system_clock;
std::time_t tt = system_clock::to_time_t (system_clock::now());
struct std::tm * ptm = std::localtime(&tt);
std::cout << "Current time: " << std::put_time(ptm,"%X") << '\n';
std::cout << "Waiting for the next minute to begin...\n";
++ptm->tm_min; ptm->tm_sec=0;
std::this_thread::sleep_until (system_clock::from_time_t (mktime(ptm)));
std::cout << std::put_time(ptm,"%X") << " reached!\n";
return 0;
}
7、std::lock_guard & std::unique_lock
成员函数
(构造函数) | 构造 lock_guard ,可选地锁定给定的互斥 (公开成员函数) |
---|---|
(析构函数) | 析构 lock_guard 对象,解锁底层互斥 (公开成员函数) |
lock_guard( mutex_type& m, std::adopt_lock_t t );的构造方法的第二个参数
std::defer_lock_t, std::try_to_lock_t, std::adopt_lock_t 三个值。是用于为 std::lock_guard 、 std::scoped_lock 、 std::unique_lock 和 std::shared_lock 指定锁定策略的空类标签类型。
defer_lock_t
不获得互斥的所有权try_to_lock_t
尝试获得互斥的所有权而不阻塞adopt_lock_t
假设调用方线程已拥有互斥的所有权
构造互斥锁的写法,就是会在lock_guard构造函数里加锁,在析构函数里解锁,达到自动加锁,解锁的目的。因此lock_guard的作用域就是锁的范围。
#include <iostream>
#include <thread>
#include <string>
#include <mutex>
using namespace std;mutex mt;
void thread_task()
{for (int i = 0; i < 10; i++){lock_guard<mutex> guard(mt);cout << "print thread: " << i << endl;}
}int main()
{thread t(thread_task);for (int i = 0; i > -10; i--){lock_guard<mutex> guard(mt);cout << "print main: " << i << endl;}t.join();return 0;
}
虽然lock_guard挺好用的,但是有个很大的缺陷,在定义lock_guard的地方会调用构造函数加锁,在离开定义域的话lock_guard就会被销毁,调用析构函数解锁。这就产生了一个问题,如果这个定义域范围很大的话,那么锁的粒度就很大,很大程序上会影响效率。所以为了解决lock_guard锁的粒度过大的原因,unique_lock就出现了。
unique_lock<mutex> unique(mt);
这个会在构造函数加锁,然后可以利用unique.unlock()来解锁,所以当你觉得锁的粒度太多的时候,可以利用这个来解锁,而析构的时候会判断当前锁的状态来决定是否解锁,如果当前状态已经是解锁状态了,那么就不会再次解锁,而如果当前状态是加锁状态,就会自动调用unique.unlock()来解锁。而lock_guard在析构的时候一定会解锁,也没有中途解锁的功能。方便肯定是有代价的,unique_lock内部会维护一个锁的状态,所以在效率上肯定会比lock_guard慢。
(构造函数) | 构造 unique_lock ,可选地锁定提供的互斥 (公开成员函数) |
---|---|
(析构函数) | 若占有关联互斥,则解锁之 (公开成员函数) |
operator= | 若占有则解锁互斥,并取得另一者的所有权 (公开成员函数) |
锁定 | |
lock | 锁定关联互斥 (公开成员函数) |
try_lock | 尝试锁定关联互斥,若互斥不可用则返回 (公开成员函数) |
try_lock_for | 试图锁定关联的可定时锁定 (TimedLockable) 互斥,若互斥在给定时长中不可用则返回 (公开成员函数) |
try_lock_until | 尝试锁定关联可定时锁定 (TimedLockable) 互斥,若抵达指定时间点互斥仍不可用则返回 (公开成员函数) |
unlock | 解锁关联互斥 (公开成员函数) |
修改器 | |
swap | 与另一 std::unique_lock 交换状态 (公开成员函数) |
release | 将关联互斥解关联而不解锁它 (公开成员函数) |
观察器 | |
mutex | 返回指向关联互斥的指针 (公开成员函数) |
owns_lock | 测试锁是否占有其关联互斥 (公开成员函数) |
operator bool | 测试锁是否占有其关联互斥 |
#include <mutex>
#include <thread>
#include <chrono>struct Box {explicit Box(int num) : num_things{num} {}int num_things;std::mutex m;
};void transfer(Box &from, Box &to, int num)
{// 仍未实际取锁std::unique_lock<std::mutex> lock1(from.m, std::defer_lock);std::unique_lock<std::mutex> lock2(to.m, std::defer_lock);// 锁两个 unique_lock 而不死锁std::lock(lock1, lock2);from.num_things -= num;to.num_things += num;// 'from.m' 与 'to.m' 互斥解锁于 'unique_lock' 析构函数
}int main()
{Box acc1(100);Box acc2(50);std::thread t1(transfer, std::ref(acc1), std::ref(acc2), 10);std::thread t2(transfer, std::ref(acc2), std::ref(acc1), 5);t1.join();t2.join();
}
8、condition_variable 条件变量
condition_variable
类是同步原语,能用于阻塞一个线程,或同时阻塞多个线程,直至另一线程修改共享变量(条件)并通知 condition_variable
。
-------------有意修改变量的线程必须
获得 std::mutex (常通过 std::lock_guard )
在保有锁时进行修改
在 std::condition_variable 上执行 notify_one 或 notify_all (不需要为通知保有锁)
即使共享变量是原子的,也必须在互斥下修改它,以正确地发布修改到等待的线程。
-----------任何有意在 std::condition_variable 上等待的线程必须
在与用于保护共享变量者相同的互斥上获得 std::unique_lock<std::mutex>
执行下列之一:
检查条件,是否为已更新或提醒它的情况
执行 wait 、 wait_for 或 wait_until ,等待操作自动释放互斥,并悬挂线程的执行。
condition_variable 被通知时,时限消失或虚假唤醒发生,线程被唤醒,且自动重获得互斥。之后线程应检查条件,若唤醒是虚假的,则继续等待。
或者
使用 wait 、 wait_for 及 wait_until 的有谓词重载,它们包揽以上三个步骤
std::condition_variable
只可与 std::unique_lock<std::mutex> 一同使用
成员函数
(构造函数) | 构造对象 (公开成员函数) |
---|---|
(析构函数) | 析构对象 (公开成员函数) |
operator=[被删除] | 不可复制赋值 (公开成员函数) |
通知 | |
notify_one | 通知一个等待的线程 (公开成员函数) |
notify_all | 通知所有等待的线程 (公开成员函数) |
等待 | |
wait | 阻塞当前线程,直到条件变量被唤醒 (公开成员函数) |
wait_for | 阻塞当前线程,直到条件变量被唤醒,或到指定时限时长后 (公开成员函数) |
wait_until | 阻塞当前线程,直到条件变量被唤醒,或直到抵达指定时间点 (公开成员函数) |
原生句柄 | |
native_handle | 返回原生句柄 |
#include <iostream>
#include <string>
#include <thread>
#include <mutex>
#include <condition_variable>std::mutex m;
std::condition_variable cv;
std::string data;
bool ready = false;
bool processed = false;void worker_thread()
{// 等待直至 main() 发送数据std::unique_lock<std::mutex> lk(m);cv.wait(lk, []{return ready;});//阻塞当前线程,直到条件变量被唤醒 (公开成员函数)// 等待后,我们占有锁。std::cout << "Worker thread is processing data\n";data += " after processing";// 发送数据回 main()processed = true;std::cout << "Worker thread signals data processing completed\n";// 通知前完成手动解锁,以避免等待线程才被唤醒就阻塞(细节见 notify_one )lk.unlock();cv.notify_one();
}int main()
{std::thread worker(worker_thread);data = "Example data";// 发送数据到 worker 线程{std::lock_guard<std::mutex> lk(m);ready = true;std::cout << "main() signals data ready for processing\n";}cv.notify_one();// 等候 worker{std::unique_lock<std::mutex> lk(m);cv.wait(lk, []{return processed;});}std::cout << "Back in main(), data = " << data << '\n';worker.join();
}
总结:C++多线程,涉及5个头文件
thread.hthread 类 (成员方法)thread 构造函数join 等待结束detach 分离线程,则无需等待结束this_thread 命名空间(静态方法)get_id 获取当前线程idyield 暂时交出cpu执行权sleep_for 睡眠等待函数sleep_until 等待到下一个具体的时间
mutex.hmutex 互斥锁lock_guard 配合互斥锁,达到互斥锁作用域范围内的自动上锁解锁unique_lock 在lock_guard基础上加了可以手动解锁的接口unlockatomic.hatomic 原子化变量atomic_flag 原子布尔类型condition_variable.hcondition_variablewait 阻塞当前线程,直到条件变量被唤醒 (公开成员函数)wait_for 阻塞当前线程,直到条件变量被唤醒,或到指定时限时长后 (公开成员函数)wait_until 阻塞当前线程,直到条件变量被唤醒,或直到抵达指定时间点 (公开成员函数)notify_ont 通知一个等待的线程 (公开成员函数)notify_all 通知所有等待的线程 (公开成员函数)condition_variable_any std::condition_variable 的泛化waitwait_forwait_untilnotify_ontnotify_all
future.hfuture 类似一个针对线程的类模板对象,可以对线程进行管理的。shared_future 不同于仅可移动的 std::future (故只有一个实例能指代任何特定的异步结果),std::shared_future 可复制而且多个 shared_future 对象能指代同一共享状态。promise 类似futurepackaged_task
相关文章:
C++11 多线程学习
C11学习 一、多线程 1、模板线程是以右值传递的 template <class Fn, class... Args> explicit thread(Fn&& fn, Args&&... args)则需要使用到std::ref和std::cref很好地解决了这个问题,std::ref 可以包装按引用传递的值。 std::cref 可以…...

数学公式测试
MVP变换 MVP变换用来描述视图变换的任务,即将虚拟世界中的三维物体映射(变换)到二维坐标中。 MVP变换分为三步: 模型变换(model tranformation):将模型空间转换到世界空间(找个好的地方,把所…...

机器学习——SVM(支持向量机)
0、前言: SVM应用:主要针对小样本数据进行学习、分类和回归(预测),能解决神经网络不能解决的过学习问题,有很好的泛化能力。(注意:SVM算法的数学原理涉及知识点比较多,所…...

【李沐深度学习笔记】基础优化方法
课程地址和说明 基础优化方法p2 本系列文章是我学习李沐老师深度学习系列课程的学习笔记,可能会对李沐老师上课没讲到的进行补充。 基础优化方法 在讲具体的线性回归实现之前,要先讲一下基础的优化模型的方法 梯度下降 当模型没有显示解(…...
tmux 配置vim风格按键,支持gbk编码
vim修改~/.tmux.conf文件,没有则新增,添加如下内容。默认前缀更改为Ctrla。强烈建议更换Caps lock键位与Ctrl键位,用过的都说好,换过就回不来了。 unbind C-b set -g prefix C-a bind a send-prefixset -sg escape-time 1bind r …...

Python —— excel文件操作(超详细)
背景 很多公司还是用excel去管理测试用例的,所以为了减少重复繁琐的导出导出工作,学会如何用代码操作excel表格很实用~ 1、读取excel文件基本步骤 1、操作excel的一些库 1、xlrd:读取库,xlwt:写入,现在…...

什么是AI问答机器人?它的应用场景有哪些?
近年来,由于技术的进步和对个性化客户体验的需求不断增长,AI问答机器人也是获得了巨大的关注。AI问答机器人,也被称为AI聊天机器人,是一种旨在模拟人类对话并通过基于文本或语音的界面与用户交互的计算机程序。其能够自动执行各种…...
静态文件
静态文件 静态文件配置 - settings.py中 1,配置静态文件的访问路径【该配置默认存在】 通过哪个url地址找静态文件 STATIC URL‘/static/’ 说明 指定访问静态文件时是需要通过/static/xxx或http://127.0.0.1:8000/static/xxx [xxx表示具体的静态资源位置] 模…...
Centos7 自部署中间件开机启动,以及java应用开机启动方法
一、zookeeper cd /etc/rc.d/init.d/ touch zookeeper chmod x zookeeper vi zookeeper#以下为内容,自行修改 路径#!/bin/bash ##chkconfig:2345 10 90#description:service zookeeper #修改为自己的目录 export ZOO_LOG_DIR/data/apache-zookeeper-3.7.0/logs…...
密度估计公式
极大似然估计: y p ( x 1 , x 2 , x 3 , . . . , x n ) 1 2 π σ e − ( x 1 − μ ) 2 2 σ 2 1 2 π σ e − ( x 2 − μ ) 2 2 σ 2 . . . 1 2 π σ e − ( x n − μ ) 2 2 σ 2 y p(x_1,x_2,x_3,...,x_n) \frac{1}{\sqrt{2\pi} \sigma} e ^{-\frac{(x_1…...
2023 ICPC 网络赛 第一场(补题:F)
7题罚时879, 队排235,校排79。 除了I题dp没注意空间限制第一发没有用滚动数组MLE,以及G题启发式合并脑抽用set当容器T一发,以及K没注意是平方的期望白wa4发这些应当避免的失误外,基本满意。剩下的题基本都是当时写不出…...
MySQL慢查询优化、日志收集定位排查、慢查询sql分析
MySQL慢查询日志收集、定位,慢查询分析、排查。 一 MySQL慢查询定位 1. 确定是否已开启慢查询日志 查看慢查询日志是否已经被开启: SHOW VARIABLES LIKE slow_query_log; 如果返回值是OFF,你需要开启它。 2. 开启慢查询日志 你可以临时在运…...
HZOJ-266:表达式计算
题目描述 给出一个表达式,其中运算符仅包含 ,-,*,/,^ 要求求出表达式的最终值。 数据可能会出现括号情况,还有可能出现多余括号情况,忽略多余括号,正常计算即可; 数据保证不会出现大于 max long int 的数据࿱…...

JavaScript学习小结
变量声明:使用var关键字,变量没有类型,但值有类型(弱类型语言) 数据类型: ①number ②string(单引号,双引号都可以表示字符串) ③boolean ④Object类型 ⑤undefine…...

MySQL学习笔记13
DISTINCT数据去重: 案例:获取tb_student学生表学员年龄的分布情况。 mysql> select * from tb_student; ------------------------------------------------- | id | name | age | gender | address | --------------------------…...
怎么获取外网ip地址
在网络连接中,每个设备都被分配一个唯一的IP地址,用于标识和定位该设备。其中,内部或局域网IP地址是在局域网内使用的,而外网IP地址则是与公共互联网通信时所使用的地址。 获取外网IP地址对于许多人来说可能是一个常见的需求&…...
算法 只出现一次的两个数字-(哈希+异或)
牛客网: BM52 题目: 数组中仅2个数字出现1次,其余出现2次 思路: 出现2次的数字异或结果为0,另外两个不同的数字异或结果res不为0,异或结果的二进制位必与其中一个相同,求出二进制位为1的pos, 遍历数组,所有此位置为1…...

外卖霸王餐小程序、H5、公众号版外卖系统源码
最新外卖霸王餐小程序、H5、微信公众号版外卖系统源码、霸王餐美团、饿了么系统,粉丝裂变玩源码下载,外卖cps小程序项目,外卖红包cps带好友返利佣金分销系统程序、饿了么美团联盟源码,外卖cps带分销返利后端源码,基于L…...
amlogic 机顶盒关闭DLNA 后,手机还能搜到盒子
S905L3 带有投屏的功能,并通过 com.droidlogic.mediacenter.dlna.MediaCenterService 服务的启动和停止来开启和关闭DLNA功能,但是在测试中发现机顶盒关闭DLNA后,手机还能搜索到盒子。我在复测中发现关闭后有时很难很久搜索到盒子,…...
@Autowire、@Recourse用啥?
在使用IDEA写Spring相关的项目的时候,在字段上使用Autowired注解时,总是会有一个波浪线提示:Field injection is not recommended. 这是为啥呢?今天就来一探究竟。 众所周知,在Spring里面有三种可选的注入方式…...
在rocky linux 9.5上在线安装 docker
前面是指南,后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …...

【网络安全产品大调研系列】2. 体验漏洞扫描
前言 2023 年漏洞扫描服务市场规模预计为 3.06(十亿美元)。漏洞扫描服务市场行业预计将从 2024 年的 3.48(十亿美元)增长到 2032 年的 9.54(十亿美元)。预测期内漏洞扫描服务市场 CAGR(增长率&…...
大语言模型如何处理长文本?常用文本分割技术详解
为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...
python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)
更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...

Reasoning over Uncertain Text by Generative Large Language Models
https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...
深度学习之模型压缩三驾马车:模型剪枝、模型量化、知识蒸馏
一、引言 在深度学习中,我们训练出的神经网络往往非常庞大(比如像 ResNet、YOLOv8、Vision Transformer),虽然精度很高,但“太重”了,运行起来很慢,占用内存大,不适合部署到手机、摄…...
小木的算法日记-多叉树的递归/层序遍历
🌲 从二叉树到森林:一文彻底搞懂多叉树遍历的艺术 🚀 引言 你好,未来的算法大神! 在数据结构的世界里,“树”无疑是最核心、最迷人的概念之一。我们中的大多数人都是从 二叉树 开始入门的,它…...
绕过 Xcode?使用 Appuploader和主流工具实现 iOS 上架自动化
iOS 应用的发布流程一直是开发链路中最“苹果味”的环节:强依赖 Xcode、必须使用 macOS、各种证书和描述文件配置……对很多跨平台开发者来说,这一套流程并不友好。 特别是当你的项目主要在 Windows 或 Linux 下开发(例如 Flutter、React Na…...

aardio 自动识别验证码输入
技术尝试 上周在发学习日志时有网友提议“在网页上识别验证码”,于是尝试整合图像识别与网页自动化技术,完成了这套模拟登录流程。核心思路是:截图验证码→OCR识别→自动填充表单→提交并验证结果。 代码在这里 import soImage; import we…...
拟合问题处理
在机器学习中,核心任务通常围绕模型训练和性能提升展开,但你提到的 “优化训练数据解决过拟合” 和 “提升泛化性能解决欠拟合” 需要结合更准确的概念进行梳理。以下是对机器学习核心任务的系统复习和修正: 一、机器学习的核心任务框架 机…...