C++中的并发与多线程:现代方法与示例

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

C++中的并发与多线程:从基础到现代标准



现代应用程序越来越需要同时执行多个任务。无论是处理服务器上的用户请求、渲染图形还是处理大数据——C++中的并发和多线程已成为开发者的必备技能。在本文中,我们将介绍C++标准库中从C++11到C++20的关键机制。您将学习如何创建线程、同步线程操作以及避免常见错误。



如果您已经熟悉C++基础知识,但想更深入地了解并行编程——本文适合您。我们不会深入探讨操作系统理论,而是专注于实用工具:std::thread、std::mutex、std::async和原子操作。



1. 多线程基础:std::thread与线程管理



在C++11中,通过std::thread类提供了标准的线程支持。创建新线程时,可以向构造函数传递可调用对象——函数、lambda表达式或函数对象。



#include <iostream>#include <thread>

void hello() { std::cout << "来自线程的问候 " << std::this_thread::get_id() << std::endl;}

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


务必为每个创建的线程调用join()或detach(),否则当std::thread对象销毁时程序会异常终止。join()会阻塞当前线程直到子线程结束,而detach()允许线程独立运行。



要向线程传递参数,请使用构造函数的额外参数:



#include <thread>#include <string>

void print_sum(int a, int b, const std::string& msg) { std::cout << msg << ": " << (a + b) << std::endl;}

int main() { std::thread t(print_sum, 3, 4, "总和"); t.join(); return 0;}


请注意:参数会被复制到线程内部。如果需要传递引用,请使用std::ref()。



2. 线程同步:互斥锁与锁



当多个线程操作共享数据时,会出现数据竞争(data race)。为了避免这种情况,需要使用同步机制。主要工具是std::mutex。



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

std::mutex mtx;int shared_counter = 0;

void increment() { for (int i = 0; i < 100000; ++i) { mtx.lock(); ++shared_counter; mtx.unlock(); }}

int main() { std::thread t1(increment); std::thread t2(increment); t1.join(); t2.join(); std::cout << "结果: " << shared_counter << std::endl; return 0;}


然而手动调用lock()和unlock()容易出错:如果在两者之间发生异常,互斥锁将保持锁定状态。最好使用RAII包装器std::lock_guard或std::unique_lock:



void increment() {    for (int i = 0; i < 100000; ++i) {        std::lock_guard<std::mutex> lock(mtx);        ++shared_counter;    }  // 离开作用域时自动解锁}


对于更灵活的控制(例如条件锁定),请使用std::unique_lock。如果需要同步多个互斥锁而不发生死锁,请使用std::lock()。



3. 异步任务:std::async与std::future



手动管理线程并不总是方便。

博客

书籍推荐