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

C++ 中的多线程与并发编程:从基础到进阶

       随着现代计算机硬件的多核发展,多线程和并发编程变得越来越重要。C++11 引入了对多线程的原生支持,提供了强大的线程管理、同步机制和并发工具,使得在 C++ 中编写高效的多线程程序变得更加容易。本文将从基础到进阶,带你深入理解 C++ 多线程编程,包括线程的创建与管理、同步机制、并发算法等。


一、C++ 多线程基础

       在 C++11 中,std::thread 是实现多线程的核心类。通过 std::thread,我们可以轻松地创建和管理线程。

1.1、创建线程

       通过 std::thread 类,创建一个新的线程并执行指定的函数。线程的生命周期由 std::thread 对象的构造和销毁管理。

示例代码:

#include <iostream>
#include <thread>

// 线程执行的函数
void printMessage() {
    std::cout << "Hello from the thread!" << std::endl;
}

int main() {
    // 创建一个线程并执行 printMessage 函数
    std::thread t(printMessage);

    // 等待线程完成
    t.join();

    std::cout << "Main thread finished." << std::endl;
    return 0;
}

输出:

Hello from the thread!
Main thread finished.

       在这个例子中,我们创建了一个线程 t 来执行 printMessage 函数。通过调用 join() 方法,主线程会等待 t 线程完成后再继续执行。


1.2、线程的分离

       std::thread 还提供了 detach() 方法,用于将线程与主线程分离,使得线程独立执行。分离后的线程无法通过 join() 等待其完成,因此主线程不再负责线程的生命周期。

示例代码:

#include <iostream>
#include <thread>

// 线程执行的函数
void printMessage() {
    std::cout << "Hello from the detached thread!" << std::endl;
}

int main() {
    // 创建并分离一个线程
    std::thread t(printMessage);
    t.detach();  // 分离线程

    std::cout << "Main thread finished." << std::endl;

    // 主线程执行完后,分离线程会继续执行,直到完成
    std::this_thread::sleep_for(std::chrono::seconds(1)); // 等待子线程执行完

    return 0;
}

输出:

Main thread finished.
Hello from the detached thread!

       在这个例子中,detach() 方法将线程从主线程分离,允许它独立执行。然而,需要注意的是,分离的线程必须在主线程结束前完成,否则它会变成一个“孤儿线程”,并可能导致程序崩溃。


二、线程同步与共享资源

       多线程编程中,多个线程可能会访问共享资源,导致竞争条件(race condition)和数据不一致问题。为了保证线程安全,我们需要使用同步机制来保护共享资源。

2.1、互斥锁(std::mutex

       std::mutex 是 C++11 引入的一个同步原语,用于防止多个线程同时访问共享资源。通过锁定互斥量,我们可以确保只有一个线程能够访问共享资源。

示例代码:

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

std::mutex mtx;  // 定义一个互斥锁
int sharedData = 0;  // 共享资源

// 线程执行的函数
void increment() {
    std::lock_guard<std::mutex> lock(mtx);  // 加锁
    ++sharedData;
    std::cout << "Shared data: " << sharedData << std::endl;
}

int main() {
    // 创建多个线程同时访问共享资源
    std::thread t1(increment);
    std::thread t2(increment);
    std::thread t3(increment);

    // 等待线程完成
    t1.join();
    t2.join();
    t3.join();

    return 0;
}

输出:

Shared data: 1
Shared data: 2
Shared data: 3

       在这个例子中,std::lock_guard<std::mutex> 用于确保对 sharedData 的访问是线程安全的。每个线程在访问共享资源之前都会加锁,确保只有一个线程可以修改 sharedData

2.2、死锁与避免

       死锁(Deadlock)是多线程编程中常见的问题,发生在两个或多个线程因为互相等待对方释放资源而永远阻塞。为了避免死锁,可以采取以下几种策略:

  1. 加锁顺序:确保所有线程以相同的顺序请求多个锁。
  2. 使用 std::lock:它能防止因加锁顺序不同而导致的死锁。

示例:

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

std::mutex mtx1, mtx2;

void task1() {
    std::lock(mtx1, mtx2);  // 同时锁定两个互斥锁
    std::lock_guard<std::mutex> lock1(mtx1, std::adopt_lock);
    std::lock_guard<std::mutex> lock2(mtx2, std::adopt_lock);

    std::cout << "Task 1 completed\n";
}

void task2() {
    std::lock(mtx1, mtx2);
    std::lock_guard<std::mutex> lock1(mtx1, std::adopt_lock);
    std::lock_guard<std::mutex> lock2(mtx2, std::adopt_lock);

    std::cout << "Task 2 completed\n";
}

int main() {
    std::thread t1(task1);
    std::thread t2(task2);

    t1.join();
    t2.join();

    return 0;
}

输出:

Task 1 completed
Task 2 completed

       通过 std::lock,我们确保了两个互斥锁 mtx1mtx2 被同时锁定,避免了死锁。


三、并发算法与 std::async

       C++11 引入了 std::asyncstd::future,使得在多线程环境中调用异步任务变得非常方便。std::async 用于启动异步任务,并返回一个 std::future 对象,用来获取任务的结果。

3.1、std::async 示例
#include <iostream>
#include <thread>
#include <future>

// 异步任务
int calculateSquare(int x) {
    std::this_thread::sleep_for(std::chrono::seconds(1));  // 模拟长时间计算
    return x * x;
}

int main() {
    // 启动异步任务
    std::future<int> result = std::async(std::launch::async, calculateSquare, 10);

    // 其他工作(模拟主线程做其他事情)
    std::cout << "Main thread is doing other work..." << std::endl;

    // 获取异步任务结果
    std::cout << "Square of 10 is: " << result.get() << std::endl;

    return 0;
}

输出:

Main thread is doing other work...
Square of 10 is: 100

       在这个例子中,std::async 启动了一个异步任务来计算 10 的平方,而主线程则可以继续执行其他工作。当我们调用 result.get() 时,主线程会等待异步任务完成并获取结果。


四、并发容器与算法

       C++17 引入了一些新的并发数据结构和算法,如 std::shared_mutexstd::scoped_lock 等,进一步简化了并发编程的复杂度。

4.1、std::shared_mutex 与读写锁

std::shared_mutex 允许多个线程并发地读取共享资源,同时确保在写操作时对资源的独占访问。

示例代码:

#include <iostream>
#include <thread>
#include <shared_mutex>
#include <vector>

std::shared_mutex rw_mutex;
std::vector<int> data;

void readData() {
    std::shared_lock<std::shared_mutex> lock(rw_mutex);  // 共享锁
    std::cout << "Reading data: ";
    for (int val : data) {
        std::cout << val << " ";
    }
    std::cout << std::endl;
}

void writeData(int val) {
    std::unique_lock<std::shared_mutex> lock(rw_mutex);  // 独占锁
    data.push_back(val);
    std::cout << "Written: " << val << std::endl;
}

int main() {
    std::thread t1(writeData, 10);
    std::thread t2(writeData, 20);
    std::thread t3(readData);
    std::thread t4(readData);

    t1.join();
    t2.join();
    t3.join();
    t4.join();

    return 0;
}

输出:

Written: 10
Written: 20
Reading data: 10 20
Reading data: 10 20

       在这个例子中,std::shared_mutex 允许多个线程同时读取数据,但在写入数据时,只有一个线程能够获得独占锁,从而确保数据的一致性。


五、总结

       C++ 的多线程编程为我们提供了强大的并发控制和线程管理能力。通过使用 std::threadstd::mutexstd::async 等工具,我们能够高效地创建和管理线程,同时保证数据的一致性和安全性。无论是简单的多线程任务,还是复杂的并发算法,C++ 都能为我们提供全面的支持。

       在实际开发中,掌握 C++ 多线程编程不仅能提升程序性能,还能帮助你更好地利用现代硬件资源。希望本文能够帮助你在 C++ 多线程编程的道路上迈出坚实的一步。


http://www.kler.cn/a/449491.html

相关文章:

  • CASA(Carnegie-Ames-Stanford Approach) 模型原理及实践
  • idea2024创建JavaWeb项目以及配置Tomcat详解
  • SQL,生成指定时间间隔内的事件次序号
  • 如何利用Python爬虫获得1688按关键字搜索商品
  • NavMeshAgent直接transform.position移动报错
  • 【ArcGIS Pro微课1000例】0064:栅格目录、栅格数据集、镶嵌数据集
  • Apache RocketMQ 5.1.3安装部署文档
  • QT多媒体开发(一):概述
  • 数据流图和流程图的区别
  • Vue.js 表单处理
  • 3.1、SDH的5种标准容器
  • CentOS常见命令
  • Note2024122001_Excel按成绩排名
  • 【YashanDB知识库】insert语句有编码不识别字,执行卡住问题
  • 掌握命令行参数的艺术:Python的`argparse`库
  • Java 连接 FTP 服务器全解析
  • 35道面向初中级前端的基础面试题
  • 汉塔上网行为管理 ping.php 远程命令执行漏洞复现(附脚本)
  • 计算机毕设-基于springboot的校园招聘网站的设计与实现(附源码+lw+ppt+开题报告)
  • Python毕业设计选题:基于Python的社区爱心养老管理系统设计与实现_django
  • VScode中配置ESlint+Prettier详细步骤(图文详情)
  • 重温设计模式--建造者模式
  • 基于矩阵乘积态的生成模型:量子力学与生成任务的结合
  • Transformer自注意力机制详解
  • Rust之抽空学习系列(五)—— 所有权(上)
  • 《点点之歌》“意外”诞生记