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

【Linux】线程池 | 自旋锁 | 读写锁

文章目录

  • 一、线程池
    • 1. 线程池模型和应用场景
    • 2. 单例模式实现线程池(懒汉模式)
  • 二、其他常见的锁
    • 1. STL、智能指针和线程安全
    • 2. 其他常见的锁
  • 三、读者写者问题
    • 1. 读者写者模型
    • 2. 读写锁


一、线程池

1. 线程池模型和应用场景

线程池是一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。

💕 线程池模型

线程池模型本质上也是生产者消费者模型,线程池的实现原理是:在线程池中预先准备好并创建一批线程,然后上层将任务push到任务队列中,休眠的线程如果检测到任务队列中有任务,就直接被操作系统唤醒,然后去消费并处理任务,唤醒一个线程的代价比创建一个线程的代价小的很多。

在这里插入图片描述

任务线程指的是生产者,任务队列指的是交易场所,右边的一大批线程指的是消费者,因此。线程池的本质还是生产消费模型。

💕 线程池的应用场景

  1. 需要大量的线程来完成任务,且完成任务的时间比较短。 WEB服务器完成网页请求这样的任务,使用线程池技术是非常合适的。因为单个任务小,而任务数量巨大,你可以想象一个热门网站的点击次数。 但对于长时间的任务,比如一个Telnet连接请求,线程池的优点就不明显了。因为Telnet会话时间比线程的创建时间大多了。
  2. 对性能要求苛刻的应用,比如要求服务器迅速响应客户请求。
  3. 接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。突发性大量客户请求,在没有线程池情况下,将产生大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,短时间内产生大量线程可能使内存到达极限,出现错误。

2. 单例模式实现线程池(懒汉模式)

💕 ThreadPool.hpp

#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <queue>
#include <unistd.h>
#include "Thread.hpp"
#include "Task.hpp"
#include "lockGuard.hpp"
using namespace std;const static int N = 5;// 将此代码设计成单例模式————懒汉模式template <class T>
class ThreadPool
{
private:ThreadPool(int num = N) : _num(num){pthread_mutex_init(&_lock, nullptr);pthread_cond_init(&_cond, nullptr);}ThreadPool(const ThreadPool<T>& tp) = delete;void operator=(const ThreadPool<T>& tp) = delete;
public:// 设计一个静态成员函数来返回创建的对象static ThreadPool<T>* getinstance(){if(_instance == nullptr){LockGuard lockguard(&_instance_lock);{if(_instance == nullptr){_instance = new ThreadPool<T>();_instance->init();_instance->start();}}}return _instance;}pthread_mutex_t *getlock(){return &_lock;}void threadWait(){pthread_cond_wait(&_cond, &_lock);}void threadWake(){pthread_cond_signal(&_cond);}bool isEmpty(){return _tasks.empty();}void init(){for (int i = 0; i < _num; i++){_threads.push_back(Thread(i + 1, threadRoutine, this));}}void start(){for (auto &t : _threads){t.run();}}void check(){for (auto &t : _threads)cout << t.threadname() << " running..." << endl;}static void threadRoutine(void *args){ThreadPool<T> *tp = static_cast<ThreadPool<T> *>(args);while (true){T t;// 检测此时有没有任务, 如果有任务就处理任务, 否则就挂起等待{LockGuard lockguard(tp->getlock());while (tp->isEmpty()){tp->threadWait();}t = tp->popTask();}t();cout << "thread handler done, result: " << t.formatRes() << endl;}}T popTask(){T t = _tasks.front();_tasks.pop();return t;}void pushTask(const T &t){LockGuard lockguard(&_lock);_tasks.push(t);threadWake();}~ThreadPool(){for (auto &t : _threads){t.join();}pthread_mutex_destroy(&_lock);pthread_cond_destroy(&_cond);}private:vector<Thread> _threads;int _num;queue<T> _tasks; // 使用stl的自动扩容机制pthread_mutex_t _lock;pthread_cond_t _cond;static ThreadPool<T>* _instance;static pthread_mutex_t _instance_lock;
};template<class T>
ThreadPool<T>* ThreadPool<T>::_instance = nullptr;template<class T>
pthread_mutex_t ThreadPool<T>::_instance_lock = PTHREAD_MUTEX_INITIALIZER;

💕 Thread.hpp

#pragma once#include <iostream>
#include <cstdlib>
#include <string>
#include <pthread.h>
using namespace std;class Thread
{
public:typedef enum{NEW = 0,RUNNING,EXITED} ThreadStatus;typedef void (*func_t)(void*);public:Thread(int num, func_t func, void* args) :_tid(0), _status(NEW),_func(func),_args(args){char name[128];snprintf(name, 128, "thread-%d", num);_name = name;}int status(){ return _status; }string threadname(){ return _name; }pthread_t get_id(){if(_status == RUNNING)return _tid;elsereturn 0;}static void* thread_run(void* args){Thread* ti = static_cast<Thread*>(args);(*ti)();return nullptr;}void operator()(){if(_func != nullptr)_func(_args);}void run() // 封装线程运行{int n = pthread_create(&_tid, nullptr, thread_run, this);if(n != 0)exit(-1);_status = RUNNING; // 线程状态变为运行}void join() // 疯转线程等待{int n = pthread_join(_tid, nullptr);if(n != 0){cout << "main thread join thread: " << _name << "error" << endl;return;}_status = EXITED;}~Thread(){}
private:pthread_t _tid;string _name;func_t _func; // 线程未来要执行的回调void* _args;ThreadStatus _status;
};

💕 Task.hpp

#pragma once
#include <iostream>
#include <string>
using namespace std;class Task
{
public:Task(){}Task(int x, int y, char op):_x(x), _y(y), _op(op), _result(0), _exitcode(0){}void operator()(){switch (_op){case '+':_result = _x + _y; break;case '-':_result = _x - _y;break;case '*':_result = _x * _y;break;case '/':{if(_y == 0)_exitcode = -1;else _result = _x / _y;}break;case '%':{if(_y == 0)_exitcode = -1;else _result = _x % _y;}break;default:break;}}string formatArge(){return to_string(_x) + _op + to_string(_y) + "=";}string formatRes(){return to_string(_result) + "(" + to_string(_exitcode) + ")";}~Task(){}private:int _x;int _y;char _op;int _result;int _exitcode;
};

💕 lockGuard.hpp

#pragma once#include <iostream>
#include <pthread.h>using namespace std;class Mutex // 自己不维护锁,有外部传入
{
public:Mutex(pthread_mutex_t *mutex):_pmutex(mutex){}void lock(){pthread_mutex_lock(_pmutex);}void unlock(){pthread_mutex_unlock(_pmutex);}~Mutex(){}
private:pthread_mutex_t *_pmutex;
};class LockGuard // 自己不维护锁,有外部传入
{
public:LockGuard(pthread_mutex_t *mutex):_mutex(mutex){_mutex.lock();}~LockGuard(){_mutex.unlock();}
private:Mutex _mutex;
};

💕 main.cc

#include "ThreadPool_V4.hpp"
#include "Task.hpp"
#include <memory>const string ops = "+-*/%";int main()
{srand(time(nullptr) ^ getpid());while(true){sleep(1);int x = rand() % 100;int y = rand() % 100;char op = ops[(x + y) % ops.size()];Task t(x, y, op);ThreadPool<Task>::getinstance()->pushTask(t);// tp->pushTask(t);cout << "the question is what: " << t.formatArge() << " ? " << endl;}return 0;
}

在这里插入图片描述


二、其他常见的锁

1. STL、智能指针和线程安全

💕 STL中的容器是否是线程安全的?

不是;原因是, STL 的设计初衷是将性能挖掘到极致, 而一旦涉及到加锁保证线程安全, 会对性能造成巨大的影响,而且对于不同的容器, 加锁方式的不同, 性能可能也不同(例如hash表的锁表和锁桶).
因此 STL 默认不是线程安全. 如果需要在多线程环境下使用, 往往需要调用者自行保证线程安全。

💕 智能指针是线程安全的吗?

智能指针是线程安全的吗?unique_ptr 是和资源强关联,只是在当前代码块范围内生效,因此不涉及线程安全问题。对于 shared_ptr,多个对象需要共有一个引用计数变量,所以会存在线程安全问题。但是标准库实现的时候也考虑到了这个问题,就基于原子操作(Compare And Swap(CAS)) 的方式保证 shared_ptr 能够高效原子地操作引用计数。shared_ptr 是线程安全的,但不意味着对其管理的资源进行操作是线程安全的,所以对 shared_ptr 管理的资源进行操作时也可能需要进行加锁保护。


2. 其他常见的锁

  • 悲观锁:悲观锁做事比较悲观,它认为多线程同时修改共享资源的概率比较高,于是很容易出现冲突,所以访问贡献资源前,先要进行加锁保护。常见的悲观锁有:互斥锁、自旋锁和读写锁等。
  • 乐观锁:乐观锁做事比较乐观,它乐观地认为共享数据不会被其他线程修改,因此不上锁。它的工作方式是:先修改完共享数据,再判断这段时间内有没有发生冲突。如果其他线程没有修改共享数据,那么则操作成功。如果发现其他线程已经修改该共享数据,就放弃本次操作。乐观锁全程并没有加锁,所以它也叫无锁编程。乐观锁主要采取两种方式:版本号机制(Gitee等)和 CAS 操作。乐观锁虽然去除了加锁和解锁的操作,但是一旦发生冲突,重试的成本是很高的,所以只有在冲突概率非常低,且加锁成本非常高的场景下,才考虑使用乐观锁。
  • CAS 操作:当需要更新数据时,判断当前内存值和之前取得的值是否相等。如果相等则用新值更新。若不等则失败,失败则重试,一般是一个自旋的过程,即不断重试。
  • 自旋锁:使用自旋锁的时候,当多线程发生竞争锁的情况时,加锁失败的线程会忙等待(这里的忙等待可以用 while 循环等待实现),直到它拿到锁。而互斥锁加锁失败后,线程会让出 CPU 资源给其他线程使用,然后该线程会被阻塞挂起。如果临界区代码执行时间过长,自旋的线程会长时间占用 CPU 资源,所以自旋的时间和临界区代码执行的时间是成正比的关系。如果临界区代码执行的时间很短,就不应该使用互斥锁,而应该选用自旋锁。因为互斥锁加锁失败,是需要发生上下文切换的,如果临界区执行的时间比较短,那可能上下文切换的时间会比临界区代码执行的时间还要长。

三、读者写者问题

1. 读者写者模型

在编写多线程的时候,有一种情况是十分常见的。那就是,有些公共数据修改的机会比较少。相比较改写,它们读的机会反而高的多。通常而言,在读的过程中,往往伴随着查找的操作,中间耗时很长。给这种代码段加锁,会极大地降低我们程序的效率。那么有没有一种方法,可以专门处理这种多读少写的情况呢?

这就需要我们的读者写者模型出场了,读者写者模型其实也是维护321原则;三种关系:读者与读者、读者与写者、写者与写者。两种对象:读者和写者。一个交易场所:需要写入和从中读取的缓冲区。

下面我们来看一下读者写者模型的三种关系:

  • 读者与读者:没有关系
  • 读者与写者:互斥与同步
  • 写者与写者:互斥

那么,为什么在生产者消费者模型中,消费者和消费者是互斥关系,而在读者写者问题中,读者和读者之间没有关系呢?

读者写者模型和生产者消费者模型的最大区别就是:消费者会将数据拿走,而读者不会拿走数据,读者仅仅是对数据做读取,并不会进行任何修改的操作,因此共享资源也不会因为有多个读者来读取而导致数据不一致的问题。


2. 读写锁

在读者写者模型中,pthread库为我们提供了 读写锁 来维护其中的同步与互斥关系。读写锁由读锁写锁两部分构成,如果只读取共享资源用读锁加锁,如果要修改共享资源则用写锁加锁。所以,读写锁适用于能明确区分读操作和写操作的场景。

读写锁的工作原理:

当写锁没有被写线程持有时,多个读线程能够并发地持有读锁,这大大提高了共享资源的访问效率。因为读锁是用于读取共享资源的场景,所以多个线程同时持有读锁也不会破坏共享资源的数据。但是,一旦写锁被写进程持有后,读线程获取读锁的操作会被阻塞,而其它写线程的获取写锁的操作也会被阻塞。

伪代码:

// 写者进程/线程执行的函数
void Writer()
{while(true){P(wCountMutex); // 进入临界区if(wCount == 0)P(rMutex); // 当第一个写者进入,如果有读者则阻塞读者wCount++;// 写者计数 + 1V(wCountMutex); // 离开临界区P(wDataMutex); // 写者写操作之间互斥,进入临界区write(); // 写数据V(wDataMutex); // 离开临界区P(wCountMutex); // 进入临界区wCount--; // 写完数据,准备离开if(wCount == 0){V(rMutex);  // 最后一个写者离开了,则唤醒读者}V(wCountMutex); //离开临界区}
}// 读者进程/线程执行的次数
void reader()
{while(TRUE){P(rMutex);P(rCountMutex); // 进入临界区if ( rCount == 0 )P(wDataMutex); // 当第一个读者进入,如果有写者则阻塞写者写操作rCount++;V(rCountMutex); // 离开临界区V(rMutex);read( ); // 读数据P(rCountMutex); // 进入临界区rCount--;if ( rCount == 0 )V(wDataMutex); // 当没有读者了,则唤醒阻塞中写者的写操作V(rCountMutex); // 离开临界区}
}

在这里插入图片描述

初始化

int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,const pthread_rwlockattr_t
*restrict attr);

销毁

int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

加锁和解锁

int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

读者写者问题很明显会存在读者优先还是写者优先的问题,如果是读者优先的话,可能就会带来写者饥饿的问题。而写者优先可以保证写线程不会饿死,但如果一直有写线程获取写锁,那么读者也会被饿死。所以使用读写锁时,需要考虑应用场景。读写锁通常用于数据被读取的频率非常高,而被修改的频率非常低。注:Linux 下的读写锁默认是读者优先的。

相关文章:

【Linux】线程池 | 自旋锁 | 读写锁

文章目录 一、线程池1. 线程池模型和应用场景2. 单例模式实现线程池(懒汉模式) 二、其他常见的锁1. STL、智能指针和线程安全2. 其他常见的锁 三、读者写者问题1. 读者写者模型2. 读写锁 一、线程池 1. 线程池模型和应用场景 线程池是一种线程使用模式。线程过多会带来调度开…...

[网鼎杯 2020 青龙组]bang 题解

写一道安卓题的WP 首先你需要一个root机&#xff0c;使用真机或者虚拟机&#xff0c;根据网上的教程刷机并获取root 我使用真机调试&#xff0c;pixel2 讲安卓包下载到真机 在PC端配置frida 对应版本的server传送到/data/local/tmp 然后进行以上操作&#xff0c;启动server …...

创建环境时提示:ERROR conda.core.link:_execute(502)

创建环境时提示&#xff1a;ERROR conda.core.link:_execute(502) 创建环境最后Executing transaction&#xff0c;失败&#xff0c;提示如下&#xff1a; Preparing transaction: done Verifying transaction: done Executing transaction: failed ERROR conda.core.link:_e…...

Python150题day07

1.5集合练习题 集合间的运算 lst1 [1, 2, 3, 5, 6, 3, 2] lst2 [2, 5, 7, 9] 哪些整数既在Ist1中&#xff0c;也在Ist2中哪些整数在Ist1中&#xff0c;不在Ist2中两个列表一共有哪些整数 虽然题目问的是两个列表之间的问题&#xff0c;但是用列表解答的效率很低&#xff0c…...

LeetCode 2596. 检查骑士巡视方案

【LetMeFly】2596.检查骑士巡视方案 力扣题目链接&#xff1a;https://leetcode.cn/problems/check-knight-tour-configuration/ 骑士在一张 n x n 的棋盘上巡视。在有效的巡视方案中&#xff0c;骑士会从棋盘的 左上角 出发&#xff0c;并且访问棋盘上的每个格子 恰好一次 。…...

大数据学习1.0-目录

学习内容持续更新ing 1.大数据学习1.1-Centos8虚拟机安装 大数据学习1.0-Centos8虚拟机安装_汉卿HanQ的博客-CSDN博客 2.大数据学习1.2-yum配置 大数据学习1.2-yum配置_汉卿HanQ的博客-CSDN博客 3.大数据学习1.3-xShell配置jdk 大数据学习1.3-xShell配置jdk_汉卿HanQ的博客…...

无涯教程-JavaScript - POWER函数

描述 POWER函数返回加到幂的数字的输出。 语法 POWER (number, power)争论 Argument描述Required/OptionalNumber 基数。 它可以是任何实数。 RequiredPowerThe exponent to which the base number is raised.Required Notes 可以使用" ^"运算符代替POWER来指示…...

ChatGPT:解释Java中 ‘HttpResponse‘ 使用 ‘try-with-resources‘ 的警告和处理 ‘Throwable‘ 打印警告

ChatGPT&#xff1a;解释Java中 ‘HttpResponse’ 使用 ‘try-with-resources’ 的警告和处理 ‘Throwable’ 打印警告 我在IDEA中对一个函数的警告点击了ignore&#xff0c;怎么撤回这个呢 ChatGPT&#xff1a; 要撤回在IDEA中对一个函数的警告的忽略&#xff0c;您可以按照以…...

Linux编辑器-gcc的使用

一&#xff1a;背景知识 1.预处理&#xff08;头文件展开、去注释、宏替换、条件编译&#xff09; 2.编译&#xff08;由C生成汇编&#xff09; 3.汇编&#xff08;生成及其可识别代码&#xff09; 4.连接&#xff08;生成可执行文件或库文件&#xff09; 二&#xff1a;gcc…...

第16篇ESP32 platformio_arduino框架 wifi联网_连接WiFi热点并连接tcp server收发数据进行通讯

第1篇:Arduino与ESP32开发板的安装方法 第2篇:ESP32 helloword第一个程序示范点亮板载LED 第3篇:vscode搭建esp32 arduino开发环境 第4篇:vscodeplatformio搭建esp32 arduino开发环境 ​​​​​​第5篇:doit_esp32_devkit_v1使用pmw呼吸灯实验 第6篇:ESP32连接无源喇叭播…...

day1| 704. 二分查找、27. 移除元素

704. 二分查找 题目链接&#xff1a;https://leetcode.cn/problems/binary-search/ 文档讲解&#xff1a;https://programmercarl.com/0704.%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE.html 视频讲解&#xff1a;https://www.bilibili.com/video/BV1fA4y1o715 1、二分法的前提 这道…...

R绘制箱线图

代码大部分来自boxplot()函数的帮助文件&#xff0c;可以通过阅读帮助文件&#xff0c;调整代码中相应参数看下效果&#xff0c;进而可以理解相应的作用&#xff0c;帮助快速掌握barplot()函数的用法。 语法 Usage(来自帮助文件) barplot(height, ...)## Default S3 method: …...

利用Audit审计系统行为

标题利用Audit审计系统行为 Linux Audit守护进程是一个可以审计Linux系统事件的框架 这个框架本身有数个组件&#xff0c;包括内核、二进制文件及其他文件。 1.内核audit&#xff1a;钩在内核中来捕获事件并将它们发送到auditd。 2.二进制文件 auditd&#xff1a;捕捉事件并…...

uniapp:不同权限设置不同的tabBar

1、在pages.json里&#xff0c;将所有tabBar涉及的页面都加进来。 我这里使用username来动态显示tabBar。 jeecg用户显示&#xff1a;首页&#xff0c;订单&#xff0c;消息&#xff0c;发现&#xff0c;我的&#xff0c;一共5个tabBar。 admin用户显示&#xff1a;首页&…...

如何将本地的项目上传到Git

一、GitHub or GitLab or Gitee创建一个新的仓库 二、仓库路径创建成功后&#xff0c;将本地项目上传到git 1. 进入本地项目所在文件夹位置&#xff0c;右击 2.出现git命令框 输入git init 在当前项目的目录中生成本地的git管理&#xff08;会发现在当前目录下多了一个.git文件…...

[php] 文件上传的一个项目emmm

项目完整地址 <!DOCTYPE html> <html lang"zh"> <head><meta charset"UTF-8"><title>上传文件</title><link href"./css/bootstrap.min.css" rel"stylesheet"><style>font-face {fo…...

uniapp-时间格式和距离格式的转换

时间格式的转换 第一种是把 YYYY-MM-DD hh:mm:ss 转换成 MM月DD日 第二种是把 hh:mm:ss 转换成 hh:mm /*** 格式化时间 1* 把传入的完整时间分为 MM月DD日 的格式* returns*/ export function formatDate(timeStr) {const date new Date(timeStr);const month (date.ge…...

【卖出备兑看涨期权策略(Covered_call)】

卖出备兑看涨期权策略&#xff08;Covered_call&#xff09; 卖出备兑看涨期权策略是一种最基本的收入策略&#xff0c;该策略主要操作就是在持有标的资产的同时卖出对应的看涨期权合约&#xff0c;以此来作为从持有的标的资产中获取租金的一种方法。如果标的资产的价格上涨到…...

【校招VIP】测试算法考点之智力分析

考点介绍&#xff1a; 智力题(逻辑分析题&#xff09;准备校招的同学们好好准备下,测试笔试中经常遇到。 测试算法考点之智力分析-相关题目及解析内容可点击文章末尾链接查看&#xff01; 一、考点试题 1.5个囚犯在装有100颗豆子的袋子里摸,他们谁的存活几率大? 5个囚犯,分…...

【Linux 服务器运维】定时任务 crontab 详解 | 文末送书

文章目录 前言一、crontab 介绍1.1 什么是 crontab1.2 crontab 命令工作流程1.3 Linux 定时任务分类 二、crontab 用法详解2.1 crond 服务安装2.2 crontab 文件内容分析2.3 crontab 命令用法2.3.1 查看定时任务列表2.3.2 编辑/创建定时任务2.3.3 删除定时任务2.3.4 其他 cronta…...

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...

盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来

一、破局&#xff1a;PCB行业的时代之问 在数字经济蓬勃发展的浪潮中&#xff0c;PCB&#xff08;印制电路板&#xff09;作为 “电子产品之母”&#xff0c;其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透&#xff0c;PCB行业面临着前所未有的挑战与机遇。产品迭代…...

模型参数、模型存储精度、参数与显存

模型参数量衡量单位 M&#xff1a;百万&#xff08;Million&#xff09; B&#xff1a;十亿&#xff08;Billion&#xff09; 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的&#xff0c;但是一个参数所表示多少字节不一定&#xff0c;需要看这个参数以什么…...

sqlserver 根据指定字符 解析拼接字符串

DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...

WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成

厌倦手动写WordPress文章&#xff1f;AI自动生成&#xff0c;效率提升10倍&#xff01; 支持多语言、自动配图、定时发布&#xff0c;让内容创作更轻松&#xff01; AI内容生成 → 不想每天写文章&#xff1f;AI一键生成高质量内容&#xff01;多语言支持 → 跨境电商必备&am…...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心

当仓库学会“思考”&#xff0c;物流的终极形态正在诞生 想象这样的场景&#xff1a; 凌晨3点&#xff0c;某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径&#xff1b;AI视觉系统在0.1秒内扫描包裹信息&#xff1b;数字孪生平台正模拟次日峰值流量压力…...

【VLNs篇】07:NavRL—在动态环境中学习安全飞行

项目内容论文标题NavRL: 在动态环境中学习安全飞行 (NavRL: Learning Safe Flight in Dynamic Environments)核心问题解决无人机在包含静态和动态障碍物的复杂环境中进行安全、高效自主导航的挑战&#xff0c;克服传统方法和现有强化学习方法的局限性。核心算法基于近端策略优化…...

[大语言模型]在个人电脑上部署ollama 并进行管理,最后配置AI程序开发助手.

ollama官网: 下载 https://ollama.com/ 安装 查看可以使用的模型 https://ollama.com/search 例如 https://ollama.com/library/deepseek-r1/tags # deepseek-r1:7bollama pull deepseek-r1:7b改token数量为409622 16384 ollama命令说明 ollama serve #&#xff1a…...

Caliper 负载(Workload)详细解析

Caliper 负载(Workload)详细解析 负载(Workload)是 Caliper 性能测试的核心部分,它定义了测试期间要执行的具体合约调用行为和交易模式。下面我将全面深入地讲解负载的各个方面。 一、负载模块基本结构 一个典型的负载模块(如 workload.js)包含以下基本结构: use strict;/…...

Docker拉取MySQL后数据库连接失败的解决方案

在使用Docker部署MySQL时&#xff0c;拉取并启动容器后&#xff0c;有时可能会遇到数据库连接失败的问题。这种问题可能由多种原因导致&#xff0c;包括配置错误、网络设置问题、权限问题等。本文将分析可能的原因&#xff0c;并提供解决方案。 一、确认MySQL容器的运行状态 …...