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

C++中多线程和互斥锁的基本使用

C++中多线程和互斥锁的基本使用

  • C++中多线程的基本使用
    • 一、 使用普通函数创建线程
    • 二、 使用 Lambda 表达式创建线程(推荐)
    • 三、 典型语法案例汇总
      • 1. 捕获引用 + 修改主线程变量
      • 2. detach 创建“后台线程”
      • 3. 使用类成员函数创建线程
      • 4. 多线程 + 互斥锁保护共享资源
    • 四、 总结
  • C++中互斥锁的基本使用
    • 一、互斥锁的作用
    • 二、未使用互斥锁 vs 使用互斥锁
    • 三、总结对比
    • 四、数据竞争分析与解决:
      • 1. 未使用互斥锁时的错误表现:
      • 2. 问题原因分析
      • 3. 互斥锁同步

参考:
C++多线程学习详解
C++多线程详解(全网最全)
带你吃透C++互斥锁

C++中多线程的基本使用

一、 使用普通函数创建线程

#include <iostream>
#include <thread>void doWork(int x) {std::cout << "Working: " << x << std::endl;
}int main() {std::thread t(doWork, 42);  // 传入函数指针和参数t.join();                   // 等待线程完成return 0;
}

特点:

  • 简单直观,适合逻辑独立的函数;
  • 无法直接访问主线程局部变量(除非使用全局或传参);

二、 使用 Lambda 表达式创建线程(推荐)

#include <iostream>
#include <thread>int main() {int value = 10;std::thread t([value]() {std::cout << "Lambda thread running: " << value << std::endl;});t.join();return 0;
}

特点:

  • 可以捕获外部变量(按值或按引用);
  • 灵活、简洁,适合小函数或带状态逻辑;
  • 推荐用于现代 C++ 多线程编程;

变量捕获方式说明

[value]() { ... }       // 值捕获(只读)
[&value]() { ... }      // 引用捕获(可读写)
[=]() { ... }           // 捕获当前作用域所有变量的副本
[&]() { ... }           // 捕获所有变量的引用
[this]() { ... }        // 捕获 this 指针(常用于类内部)

三、 典型语法案例汇总

1. 捕获引用 + 修改主线程变量

#include <iostream>
#include <thread>int main() {int result = 0;std::thread t([&result]() {result = 100;});t.join();std::cout << "Result = " << result << std::endl;return 0;
}

2. detach 创建“后台线程”

#include <iostream>
#include <thread>
#include <chrono>int main() {std::thread t([]() {std::this_thread::sleep_for(std::chrono::seconds(2));std::cout << "Background thread finished!" << std::endl;});t.detach();  // 不阻塞主线程std::cout << "Main thread exits quickly" << std::endl;return 0;
}

⚠ 使用 detach() 时要保证线程的生命周期和资源访问安全,防止访问已经被释放的变量。


3. 使用类成员函数创建线程

#include <iostream>
#include <thread>class Worker {
public:void run(int x) {std::cout << "Worker running: " << x << std::endl;}
};int main() {Worker w;std::thread t(&Worker::run, &w, 123);  // 对象地址 + 参数t.join();return 0;
}

4. 多线程 + 互斥锁保护共享资源

#include <iostream>
#include <thread>
#include <mutex>std::mutex mtx;void printSafe(int id) {std::lock_guard<std::mutex> lock(mtx);  // 自动加锁/解锁std::cout << "Thread ID: " << id << std::endl;
}int main() {std::thread t1(printSafe, 1);std::thread t2(printSafe, 2);t1.join();t2.join();return 0;
}

四、 总结

方法使用场景优点缺点
普通函数 + std::thread逻辑封装好、参数固定的线程任务简洁明了无法捕获外部变量
Lambda + std::thread动态逻辑、需要捕获状态的任务灵活、可访问外部变量、现代推荐不适合太长逻辑

C++中互斥锁的基本使用

一、互斥锁的作用

用于在多线程程序中保护共享资源,防止数据竞争(data race)

常见用法:

#include <mutex>std::mutex mtx;void threadFunc() {mtx.lock();         // 加锁// 临界区代码(访问共享资源)mtx.unlock();       // 解锁
}

更推荐的方式:使用 std::lock_guard

#include <mutex>std::mutex mtx;void threadFunc() {std::lock_guard<std::mutex> lock(mtx);  // 析构自动释放锁// 临界区代码
}

二、未使用互斥锁 vs 使用互斥锁

【未使用互斥锁的版本】(存在数据竞争)

#include <iostream>
#include <thread>int counter = 0;void add() {for (int i = 0; i < 100000; ++i) {counter++;  // 多线程同时修改,可能会出现数据竞争}
}int main() {std::thread t1(add);std::thread t2(add);t1.join();t2.join();std::cout << "Final counter (no mutex): " << counter << std::endl;return 0;
}

【使用互斥锁的版本】(避免数据竞争)

#include <iostream>
#include <thread>
#include <mutex>int counter = 0;
std::mutex mtx;void add() {for (int i = 0; i < 100000; ++i) {std::lock_guard<std::mutex> lock(mtx);counter++;}
}int main() {std::thread t1(add);std::thread t2(add);t1.join();t2.join();std::cout << "Final counter (with mutex): " << counter << std::endl;return 0;
}

三、总结对比

项目未使用互斥锁使用互斥锁(std::mutex
线程安全否,可能发生数据竞争是,线程安全
运行结果不确定,可能小于理论值稳定,符合预期值
性能较快(无锁)稍慢(加锁释放有开销)
推荐使用场景多线程只读,无共享写有共享写访问时必须使用

四、数据竞争分析与解决:

1. 未使用互斥锁时的错误表现:

日志输出异常:

[ INFO] [1754040346.741811266]: has returned trajectory with 18446744059935218265 points
[ INFO] [1754040346.741849866]: Waypoint 0: pos(0.00, 0.00, 0.00), yaw=0.00
[ INFO] [1754040346.741897766]: Waypoint 1: pos(0.00, 0.00, 0.00), yaw=0.00
...
[ INFO] [1754040346.752095449]: Waypoint 198: pos(0.00, 1385839276081958023820612523912152498000590369501888262879377677457232831957974476045551661306264153665361036034313791385840336402935561101059297341547615728513508795440121964374800002716729465953814831774142238856052736.00, 93166380607490246256839516428781872302764913110062132309905673477052295654788485977494611625942385485525609127354754662271381274992469689153543502627653304785958304676908077889204905930400618944397312.00), yaw=1012484461684370672638642220875319969585287415698854255336934621478697955025548036753087232028352492273457411087994188586700792

飞行行为异常:

  • 轨迹点数据出现极其巨大的无意义数值

  • 系统不稳定,可能出现崩溃

2. 问题原因分析

  • 写操作:后台线程在Decision()中更新latest_predict_state_

  • 读操作:主线程在StateGet()中读取latest_predict_state_

  • 无同步机制:两个线程同时访问同一块内存,导致数据损坏

当两个线程同时访问时:

  • 一个线程正在写入vector的大小信息

  • 另一个线程同时读取,可能读取到部分写入的数据

  • 导致vector的size字段被破坏,显示为SIZE_MAX

3. 互斥锁同步

修改前(无保护):

// 头文件
class Search {
private:std::vector<State> latest_predict_state_;
};// 写入操作
void Search::Decision(...) {latest_predict_state_ = execute(); // 直接写入,无保护
}// 读取操作
std::vector<State> Search::StateGet() {return latest_predict_state_; // 直接读取,无保护
}

修改后(有保护):

// 头文件
#include <mutex>class Search {
private:std::vector<State> latest_predict_state_;mutable std::mutex latest_predict_state_mutex_; // 添加互斥锁
};// 写入操作
void Search::Decision(...) {std::vector<State> new_trajectory = execute();// 在锁保护下更新共享数据{std::lock_guard<std::mutex> lock(latest_predict_state_mutex_);latest_predict_state_ = new_trajectory;}
}// 读取操作
std::vector<PredictState> Search::StateGet() {std::lock_guard<std::mutex> lock(latest_predict_state_mutex_);return latest_predict_state_; // 在锁保护下读取
}
http://www.lryc.cn/news/609164.html

相关文章:

  • 【数据结构】二叉树的顺序结构实现
  • 15_01_opencv_形态学滤波
  • 35.【.NET8 实战--孢子记账--从单体到微服务--转向微服务】--数据缓存
  • Android 之 RxJava2
  • Kali基础知识点【1】
  • 基于图像识别与分类的中国蛇类识别系统
  • gitee使用教程
  • 电路原理图绘制专业实战教程2
  • 生成式人工智能展望报告-欧盟-04-社会影响与挑战
  • Java中手动床架一个线程池
  • [硬件电路-134]:模拟电路 - 运算放大器常见运算:两模拟信号相加、相减、单模拟信号的积分、微分...
  • template<typename R = void> 意义
  • 【Python练习】075. 编写一个函数,实现简单的语音识别功能
  • golang的包和闭包
  • slice() 和 splice()
  • 决策树模型知识点整理:从原理到实战(含可视化与调参)
  • Corrosion2靶机练习笔记
  • 【图像处理基石】如何使用deepseek进行图像质量的分析?
  • 【Django】-9- 单元测试和集成测试(上)
  • Day23--回溯--39. 组合总和,40. 组合总和 II,131. 分割回文串
  • 机器人抓取流程介绍与实现——机器人抓取系统基础系列(七)
  • 深度学习中卷积与互相关
  • [ java Thread 线程 ] 由“一对一“到“一对多“
  • 【Linux网络编程基础--socket地址API】
  • 使用 Vuepress + GitHub Pages 搭建项目文档
  • GraphRAG:基于知识图谱的检索增强生成技术解析
  • 微分方程模型:用“变化率”的语言,描绘世间万物的动态演化
  • async/await和Promise之间的关系是什么?(补充)
  • 图像识别区分指定物品与其他物体
  • SelectDB数据库,新一代实时数据仓库的全面解析与应用