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

lesson9: C++多线程

1.线程库

1.1 thread类的简单介绍

C++11 中引入了对 线程的支持 了,使得 C++ 并行编程时 不需要依赖第三方库
而且在原子操作中还引入了 原子类 的概念。要使用标准库中的线程,必须包含 < thread > 头文件
函数名
功能
thread()
构造一个线程对象,没有关联任何线程函数,即没有启动任何线程
thread(fn, args1, args2, ...)
构造一个线程对象,并关联线程函数fn,args1,args2,...为线程函数的
参数
get_id()
获取线程id
jionable()
线程是否还在执行,joinable代表的是一个正在执行中的线程。
jion()
该函数调用后会 阻塞住线程 ,当该线程结束后,主线程继续执行
detach()
在创建线程对象后马上调用,用于把被创建线程与线程对象分离开,分离
的线程变为后台线程,创建的线程的"死活"就与主线程无关
  1. 线程是操作系统中的一个概念,线程对象可以关联一个线程,用来控制线程以及获取线程的
    状态。
  2. 当创建一个线程对象后,没有提供线程函数,该对象实际没有对应任何线程

1.2 线程对象关联线程函数

#include <iostream>
using namespace std;
#include <thread>
void ThreadFunc(int a)
{cout << "Thread1" << a << endl;
}
class TF
{
public:void operator()(){cout << "Thread3" << endl;}
};
int main()
{// 线程函数为函数指针thread t1(ThreadFunc, 10);// 线程函数为lambda表达式thread t2([](){cout << "Thread2" << endl; });// 线程函数为函数对象TF tf;thread t3(tf);t1.join();t2.join();t3.join();cout << "Main thread!" << endl;return 0;
}
  • 线程对象可以关联1.函数指针2.lambda表达式3.函数对象
  • 当创建一个线程对象后,没有提供线程函数,该对象实际没有对应任何线程

1.2.1 注意

  1. thread类是防拷贝的,不允许拷贝构造以及赋值,但是可以 移动构造 移动赋值 ,即将一个
    线程对象关联线程的状态转移给其他线程对象,转移期间不意向线程的执行。
  2. 可以通过jionable()函数判断线程是否是有效的,如果是以下任意情况,则线程无效
    1. 采用无参构造函数构造的线程对象
    2. 线程对象的状态已经转移给其他线程对象
    3. 线程已经调用jion或者detach结束

1.3 线程函数参数

#include<iostream>
#include<thread>
#include<mutex>
#include<condition_variable>
#include<vector>
#include<atomic>
using namespace std;void Print(int n, int& x,mutex& mtx)
{for (int i = 0; i < n; ++i){mtx.lock();cout <<this_thread::get_id()<<":"<< i << endl;std::this_thread::sleep_for(std::chrono::milliseconds(100));++x;mtx.unlock();}}int main()
{mutex m;int count = 0;thread t1(Print, 10, ref(count),ref(m));thread t2(Print, 10, ref(count),ref(m);t1.join();t2.join();cout << count << endl;return 0;
}

  •  线程函数的参数先传递给thread的,并以值拷贝的方式拷贝到线程栈空间中的
  • 如果不给线程函数的参数不借助 ref函数
    • 即使线程参数为 引用类型 ,在线程中修改后也 不能修改外部实参
    • 因为其实际引用的是线程栈中的拷贝,而不是外部实参

#include<iostream>
#include<thread>
#include<mutex>
#include<condition_variable>
#include<vector>
#include<atomic>
using namespace std;int main()
{mutex mtx;int x = 0;int n = 10;int m;cin >> m;vector<thread> v(m);//v.resize(m);for (int i = 0; i < m; ++i){// 移动赋值给vector中线程对象v[i] = thread([&](){for (int i = 0; i < n; ++i){mtx.lock();cout << this_thread::get_id() << ":" << i << endl;std::this_thread::sleep_for(std::chrono::milliseconds(100));++x;mtx.unlock();}});}for (auto& t : v){t.join();}cout << x << endl;return 0;
}
  •  借助lambda表达式中的引用捕捉也可以实现上面那个函数,就可以不用借助ref函数

1.3.1 线程并行 && 并发的讨论 

  • 并行:任务的同时进行
  • 并发: 任务的调动和切换
  • 在这个函数中其实是并行的速度更快,因为线程切换十分耗时间

1.4 原子性操作库(atomic) 

多线程最主要的问题是共享数据带来的问题 ( 即线程安全 )
当一个或多个线程要 修改 共享数据时,就会产生很多潜在的麻烦
#include<iostream>
#include<thread>
#include<mutex>
#include<condition_variable>
#include<vector>
#include<atomic>
using namespace std;int main()
{mutex mtx;atomic<int> x = 0;// int x = 0;int n = 1000000;int m;cin >> m;vector<thread> v(m);for (int i = 0; i < m; ++i){// 移动赋值给vector中线程对象v[i] = thread([&](){for (int i = 0; i < n; ++i){// t1 t2 t3 t4++x;}});}for (auto& t : v){t.join();}cout << x << endl;return 0;
}

 

  •  C++98中传统的解决方式:可以对共享修改的数据加锁保护
    • 加锁的问题: 这个线程执行的时候, 其他线程就会被阻塞,会影响程序运行的效率,而且锁如果控制不好,还容易造成死锁
  • C++11 中使用atomic类模板,定义出需要的任意原子类型
    • 程序员 不需要 对原子类型变量进行 加锁解锁 操作,线程能够对原子类型变量互斥的访问。

1.4.1 注意 

#include <atomic>
int main()
{atomic<int> a1(0);//atomic<int> a2(a1);   // 编译失败atomic<int> a2(0);//a2 = a1;               // 编译失败return 0;
}
  • 原子类型通常属于"资源型"数据,多个线程只能访问单个原子类型的拷贝,
  • 因此在C++11 中,原子类型只能从其模板参数中进行构造不允许原子类型进行拷贝构造、移动构造以及 operator=等,为了防止意外,标准库已经将atmoic模板类中的拷贝构造、移动构造、赋值运算符重载默认删除掉了 

1.5 lock_guardunique_lock  

多线程 环境下, 原子性 只能保证 某个变量的安全性

多线程环境下,而需要保证一段代码的安全性,就只能通过加锁的方式实现

1.5.1  lock_guard

#include<iostream>
#include<thread>
#include<mutex>
#include<condition_variable>
#include<vector>
#include<atomic>
using namespace std;//RAII
template<class Lock>
class LockGuard
{
public:LockGuard(Lock& lk):_lock(lk){_lock.lock();cout << "thread:" << this_thread::get_id() << "加锁" << endl;}~LockGuard(){cout << "thread:" << this_thread::get_id() << "解锁" << endl << endl;_lock.unlock();}
private:Lock& _lock;// 成员变量是引用
};int main()
{mutex mtx;atomic<int> x = 0;//int x = 0;int n = 100;int m;cin >> m;vector<thread> v(m);for (int i = 0; i < m; ++i){// 移动赋值给vector中线程对象v[i] = thread([&](){for (int i = 0; i < n; ++i){{lock_guard<mutex> lk(mtx);cout << this_thread::get_id() << ":" << i << endl;}std::this_thread::sleep_for(std::chrono::milliseconds(100));}});}for (auto& t : v){t.join();}cout << x << endl;return 0;
}
  •  lock_guard类模板主要是通过RAII的方式,对其管理的互斥量进行了封
  • 调用构造函数成功上锁,出作用域前,lock_guard对象要被销毁,调用析构函数自动解锁,可以有效避免死锁问题。
  • lock_guard 缺陷 太单一,用户没有办法对该锁进行控制

1.5.2 unique_lock 

lock_guard 不同的是, unique_lock 更加的灵活,提供了更多的成员函数
  • 上锁/解锁操作locktry_locktry_lock_fortry_lock_untilunlock
  • 修改操作:移动赋值、交换(swap:与另一个unique_lock对象互换所管理的互斥量所有 )、释放(release:返回它所管理的互斥量对象的指针,并释放所有权)
  • 获取属性owns_lock(返回当前对象是否上了锁)operator bool()(owns_lock()的功能相 )mutex(返回当前unique_lock所管理的互斥量的指针)

1.6 支持两个线程交替打印,一个打印奇数,一个打印偶数

1.6.1 错误案例

#include<iostream>
#include<thread>
#include<mutex>
#include<condition_variable>
#include<vector>
#include<atomic>
using namespace std;int main()
{int i = 0;int n = 100;mutex mtx;thread t1([&](){while (i < n){mtx.lock();cout << this_thread::get_id() << ":" << i << endl;i += 1;mtx.unlock();}});this_thread::sleep_for(chrono::microseconds(100));thread t2([&](){while (i < n){mtx.lock();cout << this_thread::get_id() << ":" << i << endl;i += 1;mtx.unlock();}});t1.join();t2.join();return 0;
}

  • 在线程切换的中间时间也会发现线程竞争抢锁的问题 

1.6.2 正确案例 

#include<iostream>
#include<thread>
#include<mutex>
#include<condition_variable>
#include<vector>
#include<atomic>
using namespace std;int main()
{int i = 0;int n = 100;mutex mtx;condition_variable cv;// 条件变量bool ready = true;// t1打印奇数thread t1([&](){while (i < n){{unique_lock<mutex> lock(mtx);cv.wait(lock, [&ready](){return !ready; });// 等待线程cout << "t1--" << this_thread::get_id() << ":" << i << endl;i += 1;ready = true;cv.notify_one();// 解除线程等待}//this_thread::yield();this_thread::sleep_for(chrono::microseconds(100));}});// t2打印偶数thread t2([&]() {while (i < n){unique_lock<mutex> lock(mtx);cv.wait(lock, [&ready](){return ready; });cout <<"t2--"<<this_thread::get_id() << ":" << i << endl;i += 1;ready = false;cv.notify_one();}});this_thread::sleep_for(chrono::seconds(3));cout << "t1:" << t1.get_id() << endl;cout << "t2:" << t2.get_id() << endl;t1.join();t2.join();return 0;
}

 

  • cv.wait(lock, [&ready]() {return !ready; });
    • ready返回的是false时,这个线程就会阻塞
    • 阻塞当前线程,并自动调用lock.unlock()允许其他锁定的线程继续执行
  •  cv.notify_one();
    • 唤醒当前线程并自动调用lock.lock();就只允许自己一个线程执行

1.7 shared_ptr的多线程问题

#include<iostream>
#include<thread>
#include<mutex>
#include<vector>
#include<atomic>
#include<memory>
using namespace std;namespace bit
{template<class T>class shared_ptr{public:shared_ptr(T* ptr = nullptr):_ptr(ptr), _pRefCount(new int(1)), _pMutex(new mutex){}shared_ptr(const shared_ptr<T>& sp):_ptr(sp._ptr), _pRefCount(sp._pRefCount), _pMutex(sp._pMutex){AddRef();}void Release(){bool flag = false;_pMutex->lock();if (--(*_pRefCount) == 0 && _ptr){cout << "delete:" << _ptr << endl;delete _ptr;delete _pRefCount;flag = true;}_pMutex->unlock();if (flag)delete _pMutex;}void AddRef(){_pMutex->lock();++(*_pRefCount);_pMutex->unlock();}shared_ptr<T>& operator=(const shared_ptr<T>& sp){if (_ptr != sp._ptr){Release();_ptr = sp._ptr;_pRefCount = sp._pRefCount;_pMutex = sp._pMutex;AddRef();}return *this;}int use_count(){return *_pRefCount;}~shared_ptr(){Release();}// 像指针一样使用T& operator*(){return *_ptr;}T* operator->(){return _ptr;}T* get() const{return _ptr;}private:T* _ptr;int* _pRefCount;// 使用时需要加锁mutex* _pMutex;// 锁指针};
}int main()
{// shared_ptr是线程安全的吗?bit::shared_ptr<double> sp1(new double(1.11));bit::shared_ptr<double> sp2(sp1);mutex mtx;vector<thread> v(2);int n = 100000;for (auto& t : v){t = thread([&](){for (size_t i = 0; i < n; ++i){// 拷贝是线程安全的bit::shared_ptr<double> sp(sp1);// 访问资源不是mtx.lock();(*sp)++;mtx.unlock();}});}for (auto& t : v){t.join();}cout << sp1.use_count() << endl;cout << *sp1 << endl;return 0;
}
  •  在多线程中,shared_ptr也应该对自己的引用计数进行加锁处理

  • 在多线程中, shared_ptr拷贝是线程安全的,但访问资源不是,所以访问资源也需要加锁

1.8 单例模式的多线程问题 

#include<iostream>
#include<thread>
#include<mutex>
using namespace std;
class Singleton
{
public:static Singleton* GetInstance(){// 保护第一次,后续不需要加锁// 双检查加锁if (_pInstance == nullptr){unique_lock<mutex> lock(_mtx);if (_pInstance == nullptr){_pInstance = new Singleton;}}return _pInstance;}private:// 构造函数私有Singleton(){};// C++11Singleton(Singleton const&) = delete;Singleton& operator=(Singleton const&) = delete;static Singleton* _pInstance;static mutex _mtx;
};Singleton* Singleton::_pInstance = nullptr;
mutex Singleton::_mtx; int main()
{Singleton::GetInstance();Singleton::GetInstance();return 0;
}
  •  在多线程的情况下, 第一次创建对象时也是需要加锁保护

1.8.1 巧妙的解决方案

#include<iostream>
#include<thread>
#include<mutex>
using namespace std;class Singleton
{
public:static Singleton* GetInstance(){static Singleton _s;// 局部的静态对象,第一次调用时初始化return &_s;}private:// 构造函数私有Singleton() {};// C++11Singleton(Singleton const&) = delete;Singleton& operator=(Singleton const&) = delete;
};int main()
{Singleton::GetInstance();Singleton::GetInstance();return 0;
}
  • 局部的静态对象,第一次调用时初始化
  • 在C++11之前是不能保证线程安全的
    静态对象的构造函数调用初始化并不能保证线程安全的原子性
  • C++11的时候修复了这个问题,所以这种写法,只能在支持C++11以后的编译器上玩

相关文章:

lesson9: C++多线程

1.线程库 1.1 thread类的简单介绍 C11 中引入了对 线程的支持 了&#xff0c;使得 C 在 并行编程时 不需要依赖第三方库 而且在原子操作中还引入了 原子类 的概念。要使用标准库中的线程&#xff0c;必须包含 < thread > 头文件 函数名 功能 thread() 构造一个线程对象…...

安卓修改SwitchCompat色值

SwitchCompat控件色值跟系统设置的主题有关&#xff0c;但是主题效果不是能轻易就能改的&#xff0c;因为涉及到整个APP的样式。网上方案基本都是通过修改style文件来改变色值&#xff0c;经过多次尝试修改最终觉得单独修改控件色值比较好。 一、控件属性 //修改开关色值就是最…...

pytorch内存泄漏

问题描述&#xff1a; 内存泄漏积累过多最终会导致内存溢出&#xff0c;当内存占用过大&#xff0c;进程会被killed掉。 解决过程&#xff1a; 在代码的运行阶段输出内存占用量&#xff0c;观察在哪一块存在内存剧烈增加或者显存异常变化的情况。但是在这个过程中要分级确认…...

20230821-字符串相乘-给树命名(unordered_map)

字符串相乘 有两个非负整数字符串num1&#xff0c;num2&#xff0c;计算num1和num2所表达整数的乘积&#xff0c;结果以字符串形式存储。注意:不能通过强制转换方法解题。 示例1&#xff1a; 输入&#xff1a; "4", "3" 输出&#xff1a; "12" …...

[Go版]算法通关村第十二关黄金——字符串冲刺题

目录 题目&#xff1a;最长公共前缀解法1&#xff1a;纵向对比-循环内套循环写法复杂度&#xff1a;时间复杂度 O ( n ∗ m ) O(n*m) O(n∗m)、空间复杂度 O ( 1 ) O(1) O(1)Go代码 解法2&#xff1a;横向对比-两两对比&#xff08;类似合并K个数组、合并K个链表&#xff09;复…...

neovim为工作区添加本地clangd配置

1 背景 尝试使用neovim开发stm32&#xff0c;使用clangd作为LSP提供代码补全等功能。 2 思路 使用stm32cubeMX生成一个基于makefile的stm32工程。 使用bear或compiledb基于makefile生成compile_commands.json文件。 为clangd配置--query-driver选项&#xff0c;使其使用arm…...

信号处理--基于EEG脑电信号的眼睛状态的分析

本实验为生物信息学专题设计小项目。项目目的是通过提供的14导联EEG 脑电信号&#xff0c;实现对于人体睁眼和闭眼两个状态的数据分类分析。每个脑电信号的时长大约为117秒。 目录 加载相关的库函数 读取脑电信号数据并查看数据的属性 绘制脑电多通道连接矩阵 绘制两类数据…...

Redis高可用:主从复制详解

目录 1.什么是主从复制&#xff1f; 2.优势 3.主从复制的原理 4.全量复制和增量复制 4.1 全量复制 4.2 增量复制 5.相关问题总结 5.1 当主服务器不进行持久化时复制的安全性 5.2 为什么主从全量复制使用RDB而不使用AOF&#xff1f; 5.3 为什么还有无磁盘复制模式&#xff…...

[Flutter]有的时候调用setState(() {})报错?

先看FlutterSDK的原生类State中有一个变量mounted。 abstract class State<T extends StatefulWidget> with Diagnosticable {/// mounted的作用是&#xff0c;此State对象当前是否在树中。/// 在创建State对象之后&#xff0c;在调用initState之前&#xff0c;框架通过…...

利用屏幕水印学习英语单词,无打扰英语单词学习

1、利用屏幕水印学习英语单词&#xff0c;不影响任何鼠标键盘操作&#xff0c;不影响工作 2、利用系统热键快速隐藏&#xff08;ALT1键 隐藏与显示&#xff09; 3、日积月累单词会有进步 4、软件下载地址: 免安装&#xff0c;代码未加密&#xff0c;安全的屏幕水印学习英语…...

开学必备物品清单!这几款优先考虑!

​马上就要开学了&#xff0c;同学们也要准备一系列开学用品&#xff0c;方便我们的学习生活&#xff0c;那有哪些数码物品可以在开学前准备的呢&#xff0c;接下来给大家安利几款很不错很实用的数码好物&#xff01; 推荐一&#xff1a;南卡00压开放式蓝牙耳机 南卡00压开放式…...

聊聊调制解调器

目录 1.什么是调制解调器 2.调制解调器的工作原理 3.调制解调器的作用 4.调制解调器未来发展 1.什么是调制解调器 调制解调器&#xff08;Modem&#xff09;是一种用于在数字设备和模拟设备之间进行数据传输的设备。调制解调器将数字数据转换为模拟信号进行传输&#xff0c;…...

Go语言入门指南:基础语法和常用特性(下)

上一节&#xff0c;我们了解Go语言特性以及第一个Go语言程序——Hello World&#xff0c;这一节就让我们更深入的了解一下Go语言的**基础语法**吧&#xff01; 一、行分隔符 在 Go 程序中&#xff0c;一行代表一个语句结束。每个语句不需要像 C 家族中的其它语言一样以分号 ;…...

【MFC常用问题记录】

MFC 记录 MFC的edit control控件显示1.控件添加变量M_edit后&#xff1a;2.控件ID为IDC_EDIT1: 线程函数使用 MFC的edit control控件显示 1.控件添加变量M_edit后&#xff1a; CString str; int x 10; str.Format(_T("%d"),x); M_edit.SetWindowText(str)2.控件ID…...

ThreadLocal内存泄漏问题

引子&#xff1a; 内存泄漏&#xff1a;是指本应该被GC回收的无用对象没有被回收&#xff0c;导致内存空间的浪费&#xff0c;当内存泄露严重时会导致内存溢出。Java内存泄露的根本原因是&#xff1a;长生命周期的对象持有短生命周期对象的引用&#xff0c;尽管短生命周期对象已…...

微服务基础概念【内含图解】

目录 拓展补充&#xff1a; 单体架构 分布式架构 面向服务的体系结构 云原生 微服务架构 什么是微服务&#xff1f; 微服务定义 拓展补充&#xff1a; 单体架构 单体架构&#xff1a;将业务的所有功能集中在一个项目中开发&#xff0c;最终打成一个包部署 优点&#x…...

Dockerfile创建 LNMP 服务+Wordpress 网站平台

文章目录 一.环境及准备工作1.项目环境2.服务器环境3.任务需求 二.Linux 系统基础镜像三.docker构建Nginx1.建立工作目录上传安装包2.编写 Dockerfile 脚本3.准备 nginx.conf 配置文件4.生成镜像5.创建自定义网络6.启动镜像容器7.验证 nginx 四.docker构建Mysql1. 建立工作目录…...

消息中间件篇

消息中间件篇 RabbitMQ 如何保证消息不丢失 面试官&#xff1a; RabbitMQ如何保证消息不丢失 候选人&#xff1a; 嗯&#xff01;我们当时MYSQL和Redis的数据双写一致性就是采用RabbitMQ实现同步的&#xff0c;这里面就要求了消息的高可用性&#xff0c;我们要保证消息的不…...

基本定时器

1.简介 1. 基本定时器 TIM6 和 TIM7 包含一个 16 位自动重载计数器 2. 可以专门用于驱动数模转换器 (DAC), 用于触发 DAC 的同步电路 3. 16 位自动重载递增计数器 4. 16 位可编程预分频器 5. 计数器溢出时, 会触发中断/DMA请求 从上往下看 1.开始RCC供给定时器的时钟 RCC_APB1…...

MySQL 中文全文检索

创建索引&#xff08;MySQL 5.7.6后全文件索引可用WITH PARSER ngram&#xff0c;针对中文&#xff0c;日文&#xff0c;韩文&#xff09; ALTER TABLE 表 ADD FULLTEXT 索引名 (字段) WITH PARSER ngram;或者CREATE FULLTEXT INDEX 索引名 ON 表 (字段) WITH PARSER ngram; …...

SciencePlots——绘制论文中的图片

文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了&#xff1a;一行…...

.Net框架,除了EF还有很多很多......

文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...

QMC5883L的驱动

简介 本篇文章的代码已经上传到了github上面&#xff0c;开源代码 作为一个电子罗盘模块&#xff0c;我们可以通过I2C从中获取偏航角yaw&#xff0c;相对于六轴陀螺仪的yaw&#xff0c;qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...

大型活动交通拥堵治理的视觉算法应用

大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动&#xff08;如演唱会、马拉松赛事、高考中考等&#xff09;期间&#xff0c;城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例&#xff0c;暖城商圈曾因观众集中离场导致周边…...

FastAPI 教程:从入门到实践

FastAPI 是一个现代、快速&#xff08;高性能&#xff09;的 Web 框架&#xff0c;用于构建 API&#xff0c;支持 Python 3.6。它基于标准 Python 类型提示&#xff0c;易于学习且功能强大。以下是一个完整的 FastAPI 入门教程&#xff0c;涵盖从环境搭建到创建并运行一个简单的…...

什么是库存周转?如何用进销存系统提高库存周转率?

你可能听说过这样一句话&#xff1a; “利润不是赚出来的&#xff0c;是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业&#xff0c;很多企业看着销售不错&#xff0c;账上却没钱、利润也不见了&#xff0c;一翻库存才发现&#xff1a; 一堆卖不动的旧货…...

[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...

pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)

目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关&#xff0…...

稳定币的深度剖析与展望

一、引言 在当今数字化浪潮席卷全球的时代&#xff0c;加密货币作为一种新兴的金融现象&#xff0c;正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而&#xff0c;加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下&#xff0c;稳定…...

保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek

文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama&#xff08;有网络的电脑&#xff09;2.2.3 安装Ollama&#xff08;无网络的电脑&#xff09;2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...