目录

一、多线程编程基础

1. 多线程的意义

2. C++11 多线程支持

二、std::thread:线程管理

1. 基本用法

2. 注意事项

三、std::mutex:互斥锁

1. 基本用法

四、std::lock_guard:RAII 风格锁管理

1. 核心优势

2. 基本用法

3. 示例:收紧锁的作用域

五、常见问题与解决方案

1. 死锁

2. 资源竞争

3. 锁的性能问题

六、最佳实践

1. 优先使用 std::lock_guard

2. 避免锁的滥用

3. 异常安全

七、总结

1. std::thread

2. std::mutex

3. std::lock_guard

4. 后续学习方向

八、示例代码汇总

示例 1:基础线程与锁

示例 2:收紧锁的作用域

示例 3:避免死锁(统一加锁顺序)

C++从入门到入土学习导航_c++学习进程-CSDN博客


一、多线程编程基础

1. 多线程的意义

  • 性能提升:利用多核 CPU 并行执行任务(如计算密集型任务)。
  • 响应性增强:在 GUI 程序中保持界面流畅(如后台计算)。
  • 复杂场景支持:模拟并发行为(如服务器处理多客户端请求)。

2. C++11 多线程支持

  • 核心组件
    • std::thread:创建和管理线程。
    • std::mutex:互斥锁,防止多线程竞争共享资源。
    • std::lock_guard:RAII 风格的锁管理工具,确保锁的自动释放。

二、std::thread:线程管理

1. 基本用法

(1) 创建线程
#include <iostream>
#include <thread>

void threadFunction() {
    std::cout << "Hello from thread!" << std::endl;
}

int main() {
    std::thread t(threadFunction); // 创建线程
    t.join(); // 等待线程结束
    return 0;
}
(2) 传递参数
void printMessage(const std::string& msg) {
    std::cout << msg << std::endl;
}

int main() {
    std::thread t(printMessage, "Hello from thread with arguments!");
    t.join();
    return 0;
}
(3) 线程分离
std::thread t([]() {
    std::cout << "Detached thread running..." << std::endl;
});
t.detach(); // 线程独立运行,主线程无需等待

2. 注意事项

  • 线程安全:避免多个线程同时修改共享资源(需配合锁使用)。
  • 资源管理:确保线程结束后释放资源(通过 join() 或 detach())。
  • 异常处理:线程函数中的异常需在内部捕获,否则可能导致程序崩溃。

三、std::mutex:互斥锁

1. 基本用法

(1) 手动加锁/解锁
#include <iostream>
#include <thread>
#include <mutex>

std::mutex mtx;
int sharedResource = 0;

void increment() {
    mtx.lock(); // 手动加锁
    sharedResource++;
    std::cout << "Value: " << sharedResource << std::endl;
    mtx.unlock(); // 手动解锁
}

int main() {
    std::thread t1(increment);
    std::thread t2(increment);
    t1.join();
    t2.join();
    return 0;
}
(2) 潜在问题
  • 忘记解锁:可能导致死锁(其他线程无法获取锁)。
  • 异常导致未解锁:如果 increment() 抛出异常,unlock() 未被调用。

四、std::lock_guard:RAII 风格锁管理

1. 核心优势

  • 自动解锁:构造时加锁,析构时解锁(无论是否抛出异常)。
  • 异常安全:避免因异常导致的死锁。
  • 简化代码:无需手动调用 lock()/unlock()

2. 基本用法

#include <iostream>
#include <thread>
#include <mutex>

std::mutex mtx;
int sharedResource = 0;

void increment() {
    std::lock_guard<std::mutex> lock(mtx); // 自动加锁
    sharedResource++;
    std::cout << "Value: " << sharedResource << std::endl;
    // 离开作用域时自动解锁
}

int main() {
    std::thread t1(increment);
    std::thread t2(increment);
    t1.join();
    t2.join();
    return 0;
}

3. 示例:收紧锁的作用域

void printBlock(int n, char c) {
    {
        std::lock_guard<std::mutex> lock(mtx); // 仅保护输出代码
        for (int i = 0; i < n; ++i) {
            std::cout << c;
        }
        std::cout << std::endl;
    } // 离开作用域时自动解锁
    // 其他非临界区代码
}

五、常见问题与解决方案

1. 死锁

(1) 原因
  • 多个线程相互等待对方持有的锁。
  • 锁的顺序不一致。
(2) 解决方案
  • 统一加锁顺序:所有线程按固定顺序申请锁。
  • 使用 std::lock:同时加锁多个互斥量,避免部分加锁失败。
    std::mutex m1, m2;
    void safeLock() {
        std::lock(m1, m2); // 同时加锁(C++14+)
        std::lock_guard<std::mutex> lock1(m1, std::adopt_lock);
        std::lock_guard<std::mutex> lock2(m2, std::adopt_lock);
    }

2. 资源竞争

(1) 表现
  • 多个线程同时修改共享资源,导致数据不一致。
(2) 解决方案
  • 使用互斥锁保护共享资源
  • 最小化锁的持有时间:仅保护关键代码段。

3. 锁的性能问题

(1) 锁粒度过大
  • 长时间持有锁会阻塞其他线程。
(2) 优化建议
  • 细粒度锁:为不同资源使用独立锁。
  • 读写锁:使用 std::shared_mutex 区分读/写操作(C++17)。

六、最佳实践

1. 优先使用 std::lock_guard

  • 替代手动 lock()/unlock():确保锁的自动释放。
  • 避免死锁风险:即使发生异常,锁也会被正确释放。

2. 避免锁的滥用

  • 仅保护共享资源:非共享数据无需加锁。
  • 减少锁的持有时间:快速执行临界区代码。

3. 异常安全

  • 在锁的作用域内捕获异常:确保资源一致性。
  • 避免在锁保护范围内执行耗时操作:防止死锁和性能下降。

七、总结

1. std::thread

  • 创建和管理线程的核心工具。
  • 需配合锁机制(如 std::mutex)实现线程安全。

2. std::mutex

  • 保护共享资源的基础工具。
  • 手动加锁/解锁需谨慎,易导致死锁或资源泄漏。

3. std::lock_guard

  • RAII 模式的核心应用,自动管理锁的生命周期。
  • 推荐作为首选锁管理工具,确保异常安全和代码简洁性。

4. 后续学习方向

  • 高级同步机制:如 std::condition_variablestd::atomic
  • 异步编程:使用 std::async 和 std::future 实现任务调度。

八、示例代码汇总

示例 1:基础线程与锁

#include <iostream>
#include <thread>
#include <mutex>

std::mutex mtx;
int counter = 0;

void incrementCounter() {
    for (int i = 0; i < 1000; ++i) {
        std::lock_guard<std::mutex> lock(mtx);
        ++counter;
    }
}

int main() {
    std::thread t1(incrementCounter);
    std::thread t2(incrementCounter);
    t1.join();
    t2.join();
    std::cout << "Final counter: " << counter << std::endl;
    return 0;
}

示例 2:收紧锁的作用域

#include <iostream>
#include <thread>
#include <mutex>

std::mutex mtx;
int sharedData = 0;

void processData() {
    // 非临界区代码
    {
        std::lock_guard<std::mutex> lock(mtx); // 保护关键代码
        sharedData += 1;
        std::cout << "Processed data: " << sharedData << std::endl;
    } // 离开作用域时自动解锁
    // 其他非临界区代码
}

int main() {
    std::thread t1(processData);
    std::thread t2(processData);
    t1.join();
    t2.join();
    return 0;
}

示例 3:避免死锁(统一加锁顺序)

#include <iostream>
#include <thread>
#include <mutex>

std::mutex m1, m2;

void safeThread() {
    std::lock(m1, m2); // 同时加锁(C++14+)
    std::lock_guard<std::mutex> lock1(m1, std::adopt_lock);
    std::lock_guard<std::mutex> lock2(m2, std::adopt_lock);
    std::cout << "Safe lock acquired" << std::endl;
}

int main() {
    std::thread t1(safeThread);
    std::thread t2(safeThread);
    t1.join();
    t2.join();
    return 0;
}

通过掌握 std::threadstd::mutexstd::lock_guard,开发者可以构建线程安全、高效并发的 C++ 程序,为更复杂的多线程场景(如异步任务、条件变量)打下坚实基础。

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐