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

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

  • C++中多线程的基本使用
    • [一、 使用普通函数创建线程](#一、 使用普通函数创建线程)
    • [二、 使用 Lambda 表达式创建线程(推荐)](#二、 使用 Lambda 表达式创建线程(推荐))
    • [三、 典型语法案例汇总](#三、 典型语法案例汇总)
      • [1. 捕获引用 + 修改主线程变量](#1. 捕获引用 + 修改主线程变量)
      • [2. detach 创建"后台线程"](#2. detach 创建“后台线程”)
      • [3. 使用类成员函数创建线程](#3. 使用类成员函数创建线程)
      • [4. 多线程 + 互斥锁保护共享资源](#4. 多线程 + 互斥锁保护共享资源)
    • [四、 总结](#四、 总结)
  • C++中互斥锁的基本使用

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

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

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

cpp 复制代码
#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 表达式创建线程(推荐)

cpp 复制代码
#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++ 多线程编程;

变量捕获方式说明

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

三、 典型语法案例汇总

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

cpp 复制代码
#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 创建"后台线程"

cpp 复制代码
#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. 使用类成员函数创建线程

cpp 复制代码
#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. 多线程 + 互斥锁保护共享资源

cpp 复制代码
#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)

常见用法:

cpp 复制代码
#include <mutex>

std::mutex mtx;

void threadFunc() {
    mtx.lock();         // 加锁
    // 临界区代码(访问共享资源)
    mtx.unlock();       // 解锁
}

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

cpp 复制代码
#include <mutex>

std::mutex mtx;

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

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

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

cpp 复制代码
#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;
}

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

cpp 复制代码
#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. 互斥锁同步

修改前(无保护):

cpp 复制代码
// 头文件
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_; // 直接读取,无保护
}

修改后(有保护):

cpp 复制代码
// 头文件
#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_; // 在锁保护下读取
}
相关推荐
weixin_3077791344 分钟前
Redis Windows迁移方案与测试
c++·windows·redis·算法·系统架构
歪歪1002 小时前
HTML 如何转 Markdown
开发语言·chrome·python·程序人生·html
zm3 小时前
bool 类型转换运算符重载
c++
小指纹3 小时前
cf--思维训练
c++·算法·macos·ios·objective-c·cocoa
小指纹3 小时前
河南萌新联赛2025第(四)场【补题】
数据结构·c++·算法·macos·objective-c·cocoa·图论
菜鸟555553 小时前
河南萌新联赛2025第四场-河南大学
c++·算法·思维·河南萌新联赛
源远流长jerry3 小时前
C++、STL面试题总结(二)
jvm·c++
路由侠内网穿透3 小时前
本地部署 SQLite 数据库管理工具 SQLite Browser ( Web ) 并实现外部访问
运维·服务器·开发语言·前端·数据库·sqlite
王者鳜錸3 小时前
PYTHON从入门到实践-18Django模版渲染
开发语言·python·django