首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >《C++ 多线程编程全解析:原理、库与实践》

《C++ 多线程编程全解析:原理、库与实践》

原创
作者头像
用户11690571
发布2025-06-11 20:43:01
发布2025-06-11 20:43:01
39300
代码可运行
举报
运行总次数:0
代码可运行

一、引言

随着多核处理器的普及,多线程编程已成为现代 C++ 应用程序开发的关键技能。C++11 起,标准库正式引入 <thread><mutex><condition_variable> 等多线程支持,极大地简化了线程创建与同步操作。

本篇文章将系统介绍 C++ 多线程编程的核心概念与实现方式,涵盖线程基础、互斥锁、条件变量、线程池设计等内容,并配合实战案例加深理解。


二、C++ 多线程基础

2.1 创建线程的基本方式

代码语言:javascript
代码运行次数:0
运行
复制
cpp复制编辑#include <iostream>
#include <thread>

void hello() {
    std::cout << "Hello from thread!\n";
}

int main() {
    std::thread t(hello);
    t.join(); // 等待线程结束
}

2.2 使用 Lambda 启动线程

代码语言:javascript
代码运行次数:0
运行
复制
cpp复制编辑std::thread t([] {
    std::cout << "Hello from lambda thread!\n";
});
t.join();

2.3 参数传递

代码语言:javascript
代码运行次数:0
运行
复制
cpp复制编辑void print_id(int id) {
    std::cout << "Thread ID: " << id << "\n";
}

std::thread t(print_id, 42);
t.join();

注意: 默认按值传递,若需引用,需使用 std::ref()


三、线程同步机制

3.1 使用互斥锁 std::mutex

代码语言:javascript
代码运行次数:0
运行
复制
cpp复制编辑#include <mutex>

std::mutex mtx;

void print_safe(int id) {
    std::lock_guard<std::mutex> lock(mtx); // 自动加锁与释放
    std::cout << "ID: " << id << "\n";
}

3.2 死锁的出现与避免

死锁常见场景:

代码语言:javascript
代码运行次数:0
运行
复制
cpp复制编辑// Thread A
lock(m1);
lock(m2);

// Thread B
lock(m2);
lock(m1); // 死锁

解决方案:统一加锁顺序或使用 std::scoped_lock / std::lock


四、条件变量 std::condition_variable

用于线程间通信(如生产者-消费者模型):

示例:生产者-消费者模型

代码语言:javascript
代码运行次数:0
运行
复制
cpp复制编辑#include <condition_variable>
#include <queue>

std::mutex mtx;
std::condition_variable cv;
std::queue<int> q;

void producer() {
    std::unique_lock<std::mutex> lock(mtx);
    q.push(1);
    cv.notify_one();
}

void consumer() {
    std::unique_lock<std::mutex> lock(mtx);
    cv.wait(lock, [] { return !q.empty(); });
    int val = q.front(); q.pop();
}

五、原子操作 std::atomic

std::atomic<T> 提供无锁并发操作:

代码语言:javascript
代码运行次数:0
运行
复制
cpp复制编辑#include <atomic>

std::atomic<int> counter = 0;

void increment() {
    for (int i = 0; i < 10000; ++i) {
        counter++;
    }
}

适用于:计数器、自旋锁、无锁数据结构等。


六、线程池实现简析

6.1 为什么需要线程池

  • 避免频繁创建/销毁线程;
  • 管理任务队列,提高资源利用率;
  • 提高吞吐量与响应速度。

6.2 简易线程池实现

代码语言:javascript
代码运行次数:0
运行
复制
cpp复制编辑class ThreadPool {
public:
    ThreadPool(size_t n) {
        for (size_t i = 0; i < n; ++i) {
            workers.emplace_back([this] {
                while (true) {
                    Task task;
                    {
                        std::unique_lock<std::mutex> lock(this->mtx);
                        this->cv.wait(lock, [this] {
                            return stop || !tasks.empty();
                        });
                        if (stop && tasks.empty()) return;
                        task = std::move(tasks.front()); tasks.pop();
                    }
                    task();
                }
            });
        }
    }

    void enqueue(Task task) {
        {
            std::unique_lock<std::mutex> lock(mtx);
            tasks.push(std::move(task));
        }
        cv.notify_one();
    }

    ~ThreadPool() {
        {
            std::unique_lock<std::mutex> lock(mtx);
            stop = true;
        }
        cv.notify_all();
        for (auto &w : workers) w.join();
    }

private:
    using Task = std::function<void()>;
    std::vector<std::thread> workers;
    std::queue<Task> tasks;
    std::mutex mtx;
    std::condition_variable cv;
    bool stop = false;
};

七、实践案例:并发文件搜索

目标

在多个线程中并发搜索给定目录中的所有 .txt 文件。

示例代码

代码语言:javascript
代码运行次数:0
运行
复制
cpp复制编辑void search_files(const std::string& path) {
    for (const auto& entry : std::filesystem::directory_iterator(path)) {
        if (entry.is_regular_file() &&
            entry.path().extension() == ".txt") {
            std::lock_guard<std::mutex> lock(mtx);
            std::cout << "Found: " << entry.path() << "\n";
        }
    }
}

int main() {
    std::vector<std::thread> threads;
    for (auto& dir : dirs) {
        threads.emplace_back(search_files, dir);
    }
    for (auto& t : threads) t.join();
}

八、线程安全与数据一致性

常见错误

错误情况

描述

竞态条件(Race)

多线程同时写入共享变量,导致结果不确定

数据竞争

一个线程写,另一个读,未同步

死锁

多线程互相等待资源,永远阻塞

解决策略

  • 使用 std::mutexstd::atomic
  • 控制共享数据粒度;
  • 避免在锁内调用用户回调(可能造成死锁)。

九、C++20 的协程与并发新特性(预览)

C++20 引入了协程(coroutine),提供比线程更轻量级的异步编程模型。配合 co_awaitco_yield 等关键词,可实现高性能的并发系统,如事件驱动服务器、网络框架等。

代码语言:javascript
代码运行次数:0
运行
复制
cpp复制编辑task<> do_work() {
    co_await some_async_operation();
    co_return;
}

十、总结

技术点

适用场景

std::thread

直接线程创建

std::mutex

共享资源保护

std::condition_variable

消息同步(如生产者消费者)

std::atomic

高性能无锁计数器

线程池

大量任务并发处理

多线程编程不仅需要掌握语法和库函数,更重要的是理解并发模型、数据一致性、资源调度等底层机制。建议读者动手实现一个线程池、并发任务队列,亲自体验并发的魅力。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、引言
  • 二、C++ 多线程基础
    • 2.1 创建线程的基本方式
    • 2.2 使用 Lambda 启动线程
    • 2.3 参数传递
  • 三、线程同步机制
    • 3.1 使用互斥锁 std::mutex
    • 3.2 死锁的出现与避免
  • 四、条件变量 std::condition_variable
    • 示例:生产者-消费者模型
  • 五、原子操作 std::atomic
  • 六、线程池实现简析
    • 6.1 为什么需要线程池
    • 6.2 简易线程池实现
  • 七、实践案例:并发文件搜索
    • 目标
    • 示例代码
  • 八、线程安全与数据一致性
    • 常见错误
    • 解决策略
  • 九、C++20 的协程与并发新特性(预览)
  • 十、总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档