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

小白零基础--CPP多线程

进程

  • 进程就是运行中的程序
  • 线程=进程中的进程

1、C++11 Thread线程库基础

#include <iostream>
#include <thread>
#include<string>void printthread(std::string msg){std::cout<<msg<<std::endl;for (int i = 0; i < 1000; i++){std::cout<<"my  "<<i<<std::endl;}return;
}int main(){//std::thread t(入口)//1、创建线程std::thread t(printthread,"hello thread");//2、保证等待线程结束,主线程在结束// t.join();//3、分离线程//t.detach();//4、joinable 判断是否可以调用joinbool isjoin = t.joinable();if(isjoin){t.join();}std::cout<<"over"<<std::endl;system( "pause");return 0;
}

2、线程函数中的数据未定义错误

2.1 临时变量

错误例子

#include <iostream>
#include <thread>void foo(int& x){x+=1;
}int main(){//std::thread t(foo,1);t.join();system( "pause");return 0;
}

正确方案

#include <iostream>
#include <thread>void foo(int& x){x+=1;
}int main(){//int i=1;std::thread t(foo,std::ref(i));t.join();std::cout<<i<<std::endl;system( "pause");return 0;
}

2.2 传递指针/引用指向局部变量

2.1 的 i 在 main中,那要是不在呢?

#include <iostream>
#include <thread>std::thread t;
void foo(int& x){x+=1;
}void externi(){int i=1;t=std::thread (foo,std::ref(i));
}int main(){//externi();t.join();system( "pause");return 0;
}

会报错
那怎么办呢?

#include <iostream>
#include <thread>std::thread t;int i=1;
void foo(int& x){x+=1;
}void externi(){t=std::thread (foo,std::ref(i));
}int main(){//externi();t.join();std::cout<<i<<std::endl;system( "pause");return 0;
}

2.3 参数被提前手动释放

智能指针

#include <iostream>
#include <thread>
#include <memory>class  myclass
{
private:/* data */
public:void foo(){std::cout<<"mememem"<<std::endl;}
};int main(){std::shared_ptr<myclass> a=std::make_shared<myclass> ();std::thread t(&myclass::foo,a);system( "pause");return 0;
}

2.4 类的private函数

友元

#include <iostream>
#include <thread>
#include <memory>class  myclass
{
private:friend void foo_thread();void foo(){std::cout<<"mememem"<<std::endl;}
public:};
void foo_thread(){std::shared_ptr<myclass> a=std::make_shared<myclass> ();std::thread t(&myclass::foo,a);t.join();
}
int main(){foo_thread();system( "pause");return 0;
}

3互斥量

3.1数据共享–数据竞争问题

#include <iostream>
#include <thread>int a = 0;
void func(){for (int i = 0; i < 1000000; i++){a+=1;}}
int main(){std::thread t1(func);std::thread t2(func);t1.join();t2.join();std::cout<<a<<std::endl;system( "pause");return 0;
}

3.2 互斥锁

#include <iostream>
#include <thread>
#include <mutex>
int a = 0;std::mutex mt;
void func(){for (int i = 0; i < 1000000; i++){ mt.lock();a+=1;mt.unlock();}}
int main(){std::thread t1(func);std::thread t2(func);t1.join();t2.join();std::cout<<a<<std::endl;system( "pause");return 0;
}

3.3 理解线程安全

4互斥量死锁

4.1 死锁的概念

#include <iostream>
#include <thread>
#include <mutex>
using   namespace std;mutex m1,m2;
void func1(){for (int i = 0; i < 100000; i++){ m1.lock();m2.lock();m1.unlock();m2.unlock();}}
void func2(){for (int i = 0; i < 100000; i++){ m1.lock();m2.lock();m2.unlock();m1.unlock();}
}
int main(){thread t1(func1);thread t2(func2);t1.join();t2.join();cout<<"over<<"<<endl;system( "pause");return 0;
}

4.2 解决方案

5 lock_guard 和 std::unique_lock

5.1 lock_guard

#include <iostream>
#include <thread>
#include <mutex>
using   namespace std;
int a=0;
mutex m1;
void func1(){for(int i=0;i<10000;i++){lock_guard<mutex> gm(m1);a++;}  
}int main(){thread t1(func1);t1.join();cout<<a<<endl;system( "pause");return 0;
}

5.2 std::unique_lock

#include <iostream>
#include <thread>
#include <mutex>
using   namespace std;
int a=0;
mutex m1;
void func1(){for(int i=0;i<10000;i++){unique_lock<mutex> gm(m1);a++;}  
}int main(){thread t1(func1);t1.join();cout<<a<<endl;system( "pause");return 0;
}

6 call_once

6.1 单例模式

6.2 例子:日志类

#include <iostream>
#include <thread>
#include <mutex>
using   namespace std;
class Log{
public:Log(){};Log(const Log& log)=delete;Log &operator =(const Log& log)=delete;static Log& GetInstance(){static Log  log;//懒汉模式return log;//饿汉模式/**static Log   *log=nullptr;if(!log) log = new Log;return *log;*/}void PrintLog(string msg){cout << __TIME__ <<" " <<msg<<endl;}
};int main(){Log::GetInstance().PrintLog("error");system( "pause");return 0;
}

6.3 call_once

#include <iostream>
#include <thread>
#include <mutex>
#include <vector>using namespace std;// 需要一次性初始化的共享资源
class DatabaseConfig {
private:string serverAddress;int port;DatabaseConfig() : serverAddress("127.0.0.1"), port(3306) {cout << "数据库配置初始化完成!" << endl;}public:static DatabaseConfig& getInstance() {static once_flag initFlag;static DatabaseConfig* instance = nullptr;call_once(initFlag, []() {instance = new DatabaseConfig();});return *instance;}void showConfig() {cout << "Server: " << serverAddress << ":" << port << endl;}
};// 多线程测试函数
void threadTask(int id) {this_thread::sleep_for(chrono::milliseconds(100 * id));auto& config = DatabaseConfig::getInstance();cout << "线程" << id << "获取配置:";config.showConfig();
}int main() {vector<thread> threads;// 创建10个线程竞争访问for(int i = 0; i < 10; ++i) {threads.emplace_back(threadTask, i);}// 等待所有线程完成for(auto& t : threads) {t.join();}system("pause");return 0;
}
  • 合理使用 call_once 可以让多线程代码更简洁、更安全,尤其适合需要一次性初始化的场景

7 condition_variable

7.1 生产者-消费者模式概述

生产者-消费者模式是多线程编程中经典的同步问题,需要满足以下条件:

  1. 生产者线程生成数据并放入共享缓冲区。
  2. 消费者线程从缓冲区取出数据并处理。
  3. 同步要求
    • 缓冲区满时,生产者等待消费者消费数据。
    • 缓冲区空时,消费者等待生产者生产数据。

7.2 核心组件

  1. 共享缓冲区:通常使用队列(std::queue)实现。
  2. 互斥锁(std::mutex:保护对缓冲区的并发访问。
  3. 条件变量(std::condition_variable
    • not_full:生产者等待缓冲区非满。
    • not_empty:消费者等待缓冲区非空。

7.3实现代码

#include <iostream>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>using namespace std;const int BUFFER_SIZE = 5;       // 缓冲区容量
queue<int> buffer;               // 共享缓冲区
mutex mtx;                       // 互斥锁
condition_variable not_full;     // 缓冲区非满条件
condition_variable not_empty;    // 缓冲区非空条件// 生产者函数
void producer(int id) {for (int i = 0; i < 10; ++i) {unique_lock<mutex> lock(mtx);// 如果缓冲区满,等待消费者消费not_full.wait(lock, [] { return buffer.size() < BUFFER_SIZE; });// 生产数据int data = id * 100 + i;buffer.push(data);cout << "生产者 " << id << " 生产数据: " << data << endl;lock.unlock();not_empty.notify_one();  // 通知消费者this_thread::sleep_for(chrono::milliseconds(100));}
}// 消费者函数
void consumer(int id) {for (int i = 0; i < 10; ++i) {unique_lock<mutex> lock(mtx);// 如果缓冲区空,等待生产者生产not_empty.wait(lock, [] { return !buffer.empty(); });// 消费数据int data = buffer.front();buffer.pop();cout << "消费者 " << id << " 消费数据: " << data << endl;lock.unlock();not_full.notify_one();   // 通知生产者this_thread::sleep_for(chrono::milliseconds(200));}
}int main() {thread producers[2];thread consumers[3];// 启动2个生产者线程for (int i = 0; i < 2; ++i) {producers[i] = thread(producer, i);}// 启动3个消费者线程for (int i = 0; i < 3; ++i) {consumers[i] = thread(consumer, i);}// 等待所有线程结束for (auto& t : producers) t.join();for (auto& t : consumers) t.join();return 0;
}

7.4 代码解析

  1. 共享资源保护

    • 所有对缓冲区的操作(pushpop)均在互斥锁mtx的保护下进行。
    • 使用unique_lock自动管理锁的生命周期。
  2. 条件变量的使用

    • 生产者等待条件not_full.wait(lock, predicate)
      当缓冲区满时(buffer.size() >= BUFFER_SIZE),生产者线程阻塞,直到消费者消费数据后通过not_full.notify_one()唤醒。
    • 消费者等待条件not_empty.wait(lock, predicate)
      当缓冲区空时(buffer.empty()),消费者线程阻塞,直到生产者生产数据后通过not_empty.notify_one()唤醒。
  3. 通知机制

    • 生产者生产数据后调用not_empty.notify_one(),唤醒一个等待的消费者。
    • 消费者消费数据后调用not_full.notify_one(),唤醒一个等待的生产者。

7.5 运行结果示例

生产者 0 生产数据: 0
消费者 0 消费数据: 0
生产者 1 生产数据: 100
消费者 1 消费数据: 100
生产者 0 生产数据: 1
消费者 2 消费数据: 1
...
(输出将展示生产与消费的交替过程)

7.6 关键点总结

  1. 防止虚假唤醒
    条件变量的wait必须配合谓词(如buffer.size() < BUFFER_SIZE)使用,确保即使被意外唤醒也能重新检查条件。

  2. 资源管理

    • unique_lockwait时自动释放锁,唤醒后重新获取锁。
    • 使用notify_one而非notify_all,减少不必要的线程竞争。
  3. 死锁避免

    • 确保在调用notify_one前释放锁(通过lock.unlock())。
    • 避免在持有锁时进行耗时操作(如示例中的sleep_for在锁外执行)。

7.7 扩展场景

  • 多生产者和多消费者
    当前代码已支持多个生产者和消费者,通过调整线程数量即可验证。

  • 动态缓冲区大小
    可将BUFFER_SIZE设为动态值,根据需求调整。

  • 复杂数据类型
    queue<int>替换为自定义数据类型队列,实现更复杂的生产-消费逻辑。

此实现完整展示了如何利用condition_variable实现线程安全的生产者-消费者模式,可直接用于实际项目中的任务队列、线程池等场景。

8 跨平台线程池

#include <vector>
#include <queue>
#include <memory>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <future>
#include <functional>
#include <stdexcept>class ThreadPool {
public:// 构造函数,启动指定数量的工作线程ThreadPool(size_t threads = std::thread::hardware_concurrency()): stop(false) {for(size_t i = 0; i < threads; ++i)workers.emplace_back([this] {for(;;) {std::function<void()> task;{std::unique_lock<std::mutex> lock(this->queue_mutex);this->condition.wait(lock,[this]{ return this->stop || !this->tasks.empty(); });if(this->stop && this->tasks.empty())return;task = std::move(this->tasks.front());this->tasks.pop();}task();}});}// 将任务添加到任务队列,返回一个future以便获取结果template<class F, class... Args>auto enqueue(F&& f, Args&&... args) -> std::future<typename std::result_of<F(Args...)>::type> {using return_type = typename std::result_of<F(Args...)>::type;auto task = std::make_shared< std::packaged_task<return_type()> >(std::bind(std::forward<F>(f), std::forward<Args>(args)...));std::future<return_type> res = task->get_future();{std::unique_lock<std::mutex> lock(queue_mutex);// 不允许在停止线程池后添加新任务if(stop)throw std::runtime_error("enqueue on stopped ThreadPool");tasks.emplace([task](){ (*task)(); });}condition.notify_one();return res;}// 析构函数,等待所有任务完成并停止所有线程~ThreadPool() {{std::unique_lock<std::mutex> lock(queue_mutex);stop = true;}condition.notify_all();for(std::thread &worker : workers)worker.join();}private:std::vector<std::thread> workers;     // 工作线程集合std::queue<std::function<void()>> tasks; // 任务队列std::mutex queue_mutex;               // 任务队列互斥锁std::condition_variable condition;    // 条件变量bool stop;                            // 停止标志
};// 使用示例
int main() {ThreadPool pool(4); // 创建4个工作线程// 提交多个任务到线程池std::vector<std::future<int>> results;for(int i = 0; i < 8; ++i) {results.emplace_back(pool.enqueue([i] {std::this_thread::sleep_for(std::chrono::seconds(1));return i*i;}));}// 获取任务结果for(auto && result : results)std::cout << result.get() << ' ';std::cout << std::endl;return 0;
}

9 异步并发 async future packaged task promise

#include <iostream>
#include <future>
#include <thread>
#include <chrono>
#include <vector>int compute(int x) {std::this_thread::sleep_for(std::chrono::seconds(1)); // 模拟耗时操作return x * x;
}int main() {// 使用 std::async 启动多个异步任务std::vector<std::future<int>> futures;for (int i = 1; i <= 5; ++i) {futures.push_back(std::async(std::launch::async, compute, i));}// 获取所有任务的结果for (auto& future : futures) {std::cout << "Result: " << future.get() << std::endl;}// 使用 std::packaged_task 手动控制任务执行std::packaged_task<int(int)> task(compute);std::future<int> future = task.get_future();std::thread t(std::move(task), 10);t.join(); // 等待线程完成std::cout << "Packaged Task Result: " << future.get() << std::endl;return 0;
}

10 原子操作atomic

#include <iostream>
#include <thread>
#include <atomic>
using namespace std;atomic<int> a(0); // 使用 atomic<int> 替代普通 intvoid func1() {for (int i = 0; i < 10000; i++) {a++; // 原子操作,无需额外的互斥锁}
}int main() {thread t1(func1);t1.join();cout << a << endl; // 输出最终结果system("pause");return 0;
}

相关文章:

小白零基础--CPP多线程

进程 进程就是运行中的程序线程进程中的进程 1、C11 Thread线程库基础 #include <iostream> #include <thread> #include<string>void printthread(std::string msg){std::cout<<msg<<std::endl;for (int i 0; i < 1000; i){std::cout<…...

Java线程认识和Object的一些方法ObjectMonitor

专栏系列文章地址&#xff1a;https://blog.csdn.net/qq_26437925/article/details/145290162 本文目标&#xff1a; 要对Java线程有整体了解&#xff0c;深入认识到里面的一些方法和Object对象方法的区别。认识到Java对象的ObjectMonitor&#xff0c;这有助于后面的Synchron…...

LeetCode:300.最长递增子序列

跟着carl学算法&#xff0c;本系列博客仅做个人记录&#xff0c;建议大家都去看carl本人的博客&#xff0c;写的真的很好的&#xff01; 代码随想录 LeetCode&#xff1a;300.最长递增子序列 给你一个整数数组 nums &#xff0c;找到其中最长严格递增子序列的长度。 子序列 是由…...

pytorch实现长短期记忆网络 (LSTM)

人工智能例子汇总&#xff1a;AI常见的算法和例子-CSDN博客 LSTM 通过 记忆单元&#xff08;cell&#xff09; 和 三个门控机制&#xff08;遗忘门、输入门、输出门&#xff09;来控制信息流&#xff1a; 记忆单元&#xff08;Cell State&#xff09; 负责存储长期信息&…...

AI学习指南HuggingFace篇-模型部署与推理

一、引言 将训练好的模型部署为API并实现推理是将AI模型应用于实际场景的关键步骤。Hugging Face提供了多种工具和框架,支持快速部署和优化模型推理。本文将介绍如何将Hugging Face模型部署为API,探讨模型部署的常见方法和优化技巧,帮助读者将模型应用于实际场景。 二、模型…...

Games104——引擎工具链高级概念与应用

世界编辑器 其实是一个平台&#xff08;hub&#xff09;&#xff0c;集合了所有能够制作地形世界的逻辑 editor viewport&#xff1a;可以说是游戏引擎的特殊视角&#xff0c;会有部分editor only的代码&#xff08;不小心开放就会变成外挂入口&#xff09;Editable Object&…...

【PyQt】学习PyQt进行GUI开发从基础到进阶逐步掌握详细路线图和关键知识点

学习PyQt的必要性 PyQt是开发跨平台GUI应用的强大工具&#xff0c;适合需要构建复杂、高性能界面的开发者。无论是职业发展还是项目需求&#xff0c;学习PyQt都具有重要意义。 1. 跨平台GUI开发 跨平台支持&#xff1a;PyQt基于Qt框架&#xff0c;支持Windows、macOS、Linux…...

消息队列应用示例MessageQueues-STM32CubeMX-FreeRTOS《嵌入式系统设计》P343-P347

消息队列 使用信号量、事件标志组和线标志进行任务同步时&#xff0c;只能提供同步的时刻信息&#xff0c;无法在任务之间进行数据传输。要实现任务间的数据传输&#xff0c;一般使用两种方式&#xff1a; 1. 全局变量 在 RTOS 中使用全局变量时&#xff0c;必须保证每个任务…...

Hive之数据定义DDL

Hive之数据定义DDL 文章目录 Hive之数据定义DDL写在前面创建数据库查询数据库显示数据库查看数据库详情切换当前数据库 修改数据库删除数据库创建表管理表(内部表)外部表管理表与外部表的互相转换 修改表重命名表增加、修改和删除表分区增加/修改/替换列信息 删除表 写在前面 …...

ROS-SLAM

基本概念 SLAM 即 Simultaneous Localization and Mapping&#xff0c;中文名为同时定位与地图构建&#xff0c;是机器人、自动驾驶、增强现实等领域中的关键技术。 在未知环境中&#xff0c;搭载特定传感器的主体&#xff08;如机器人、无人机等&#xff09;在运动过程中&am…...

网络攻防实战指北专栏讲解大纲与网络安全法

专栏 本专栏为网络攻防实战指北&#xff0c;大纲如下所示 进度&#xff1a;目前已更完准备篇、HTML基础 计划&#xff1a;所谓基础不牢&#xff0c;地动山摇。所以下一步将持续更新基础篇内容 讲解信息安全时&#xff0c;结合《中华人民共和国网络安全法》&#xff08;以下简…...

Spark的基本概念

个人博客地址&#xff1a;Spark的基本概念 | 一张假钞的真实世界 编程接口 RDD&#xff1a;弹性分布式数据集&#xff08;Resilient Distributed Dataset &#xff09;。Spark2.0之前的编程接口。Spark2.0之后以不再推荐使用&#xff0c;而是被Dataset替代。Dataset&#xff…...

效用曲线的三个实例

效用曲线的三个实例 文章目录 效用曲线的三个实例什么是效用曲线风险与回报&#xff1a;投资决策消费选择&#xff1a;价格与质量的平衡程序员绩效评估&#xff1a;准时与程序正确性 分析- 风险与回报&#xff1a;投资决策分析- 消费选择&#xff1a;价格与质量的平衡- 程序员绩…...

JavaScript面向对象编程:Prototype与Class的对比详解

JavaScript面向对象编程&#xff1a;Prototype与Class的对比详解 JavaScript面向对象编程&#xff1a;Prototype与Class的对比详解引言什么是JavaScript的面向对象编程&#xff1f;什么是Prototype&#xff1f;Prototype的定义Prototype的工作原理示例代码优点缺点 什么是JavaS…...

neo4j-community-5.26.0 create new database

1.edit neo4j.conf 把 # The name of the default database initial.dbms.default_databasehonglouneo4j # 写上自己的数据库名称 和 # Name of the service #5.0 server.windows_service_nameneo4j #4.0 dbms.default_databaseneo4j #dbms.default_databaseneo4jwind serve…...

pytorch实现门控循环单元 (GRU)

人工智能例子汇总&#xff1a;AI常见的算法和例子-CSDN博客 特性GRULSTM计算效率更快&#xff0c;参数更少相对较慢&#xff0c;参数更多结构复杂度只有两个门&#xff08;更新门和重置门&#xff09;三个门&#xff08;输入门、遗忘门、输出门&#xff09;处理长时依赖一般适…...

有没有个性化的UML图例

绿萝小绿萝 (53****338) 2012-05-10 11:55:45 各位大虾&#xff0c;有没有个性化的UML图例 绿萝小绿萝 (53****338) 2012-05-10 11:56:03 例如部署图或时序图的图例 潘加宇 (35***47) 2012-05-10 12:24:31 "个性化"指的是&#xff1f; 你的意思使用你自己的图标&…...

在CentOS服务器上部署DeepSeek R1

在CentOS服务器上部署DeepSeek R1,并通过公网IP与其进行对话,可以按照以下步骤操作: 一、环境准备 系统要求: CentOS 8+(需支持AVX512指令集)。 硬件配置: GPU版本:NVIDIA驱动520+,CUDA 11.8+。 CPU版本:至少16核处理器,64GB内存。 存储空间:原始模型需要30GB,量…...

Vue3.0实战:大数据平台可视化

文章目录 创建vue3.0项目项目初始化项目分辨率响应式设置项目顶部信息条创建页面主体创建全局引入echarts和axios后台接口创建express销售总量图实现完整项目下载项目任何问题都可在评论区,或者直接私信即可。 创建vue3.0项目 创建项目: vue create vueecharts选择第三项:…...

洛谷 P1130 红牌 C语言

题目描述 某地临时居民想获得长期居住权就必须申请拿到红牌。获得红牌的过程是相当复杂&#xff0c;一共包括 N 个步骤。每一步骤都由政府的某个工作人员负责检查你所提交的材料是否符合条件。为了加快进程&#xff0c;每一步政府都派了 M 个工作人员来检查材料。不幸的是&…...

语音识别播报人工智能分类垃圾桶(论文+源码)

2.1 需求分析 本次语音识别播报人工智能分类垃圾桶&#xff0c;设计功能要求如下∶ 1、具有四种垃圾桶&#xff0c;分别为用来回收厨余垃圾&#xff0c;有害垃圾&#xff0c;可回收垃圾&#xff0c;其他垃圾。 2、当用户语音说出“旧报纸”&#xff0c;“剩菜”等特定词语时…...

MVC、MVP和MVVM模式

MVC模式中&#xff0c;视图和模型之间直接交互&#xff0c;而MVP模式下&#xff0c;视图与模型通过Presenter进行通信&#xff0c;MVVM则采用双向绑定&#xff0c;减少手动同步视图和模型的工作。每种模式都有其优缺点&#xff0c;适合不同规模和类型的项目。 ### MVVM 与 MVP…...

shiro学习五:使用springboot整合shiro。在前面学习四的基础上,增加shiro的缓存机制,源码讲解:认证缓存、授权缓存。

文章目录 前言1. 直接上代码最后在讲解1.1 新增的pom依赖1.2 RedisCache.java1.3 RedisCacheManager.java1.4 jwt的三个类1.5 ShiroConfig.java新增Bean 2. 源码讲解。2.1 shiro 缓存的代码流程。2.2 缓存流程2.2.1 认证和授权简述2.2.2 AuthenticatingRealm.getAuthentication…...

负载均衡器高可用部署

Haproxy 和 Keepalived安装Haproxy配置文件准备Keepalived配置及健康检查启动Haproxy & Keepalived服务继续上一篇文章《K8S集群架构及主机准备》,下面介绍负载均衡器搭建过程 Haproxy 和 Keepalived安装 在负载均衡器两个主机上安装即可 apt install haproxy keepalived…...

属性编程与权限编程

问题 如何获取文件的大小&#xff0c;时间戳以及类型等信息&#xff1f; 再论 inode 文件的物理载体是硬盘&#xff0c;硬盘的最小存储单元是扇区 (每个扇区 512 字节) 文件系统以 块 为单位(每个块 8 个扇区) 管理文件数据 文件元信息 (创建者、创建日期、文件大小&#x…...

用 HTML、CSS 和 JavaScript 实现抽奖转盘效果

顺序抽奖 前言 这段代码实现了一个简单的抽奖转盘效果。页面上有一个九宫格布局的抽奖区域&#xff0c;周围八个格子分别放置了不同的奖品名称&#xff0c;中间是一个 “开始抽奖” 的按钮。点击按钮后&#xff0c;抽奖区域的格子会快速滚动&#xff0c;颜色不断变化&#xf…...

R语言绘制有向无环图(DAG)

有向无环图&#xff08;Directed Acyclic Graph&#xff0c;简称DAG&#xff09;是一种特殊的有向图&#xff0c;它由一系列顶点和有方向的边组成&#xff0c;其中不存在任何环路。这意味着从任一顶点出发&#xff0c;沿着箭头方向移动&#xff0c;你永远无法回到起始点。 从流…...

报错Too many open files

1、先查看系统最大打开文件数 # 查看当前系统打开文件最大数 # ulimit -a core file size (blocks, -c) 0 data seg size (kbytes, -d) unlimited scheduling priority (-e) 0 file size (blocks, -f) unlimited pending signal…...

Spring Web MVC基础第一篇

目录 1.什么是Spring Web MVC&#xff1f; 2.创建Spring Web MVC项目 3.注解使用 3.1RequestMapping&#xff08;路由映射&#xff09; 3.2一般参数传递 3.3RequestParam&#xff08;参数重命名&#xff09; 3.4RequestBody&#xff08;传递JSON数据&#xff09; 3.5Pa…...

129.求根节点到叶节点数字之和(遍历思想)

Problem: 129.求根节点到叶节点数字之和 文章目录 题目描述思路复杂度Code 题目描述 思路 遍历思想(利用二叉树的先序遍历) 直接利用二叉树的先序遍历&#xff0c;将遍历过程中的节点值先利用字符串拼接起来遇到根节点时再转为数字并累加起来&#xff0c;在归的过程中&#xf…...