C++多线程编程简介

C++多线程编程

概述

多线程编程允许在同一个程序中同时执行多个任务,从而提高程序的效率,特别是在处理 I/O 密集型或计算密集型任务时。C++11 引入了标准库中的多线程支持,使得 C++ 开发者能够更加方便地进行多线程编程。

线程基础

线程是程序中执行的最小单位,每个线程有自己的程序计数器、堆栈和局部变量。C++ 的 库提供了对线程的支持,允许我们在程序中启动多个并行的线程。

线程的创建与管理

要创建一个线程,首先需要包含 头文件。通过 std::thread 类可以创建线程。

创建线程

cpp 复制代码
#include <iostream>
#include <thread>

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

int main() {
    std::thread t(print_hello);  // 创建一个线程,调用 print_hello 函数
    t.join();  // 等待线程执行完成
    return 0;
}

解释:

  • std::thread 构造函数接收一个函数作为参数,该函数将在新线程中执行。
  • t.join() 会阻塞主线程,直到 t 所表示的线程执行完成。
  • join 是线程同步的一种方式,确保主线程等待子线程完成后再继续执行。

多个线程

可以创建多个线程来执行不同的任务。每个线程都会执行一个独立的任务。

cpp 复制代码
#include <iostream>
#include <thread>

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

void print_world() {
    std::cout << "World from thread!" << std::endl;
}

int main() {
    std::thread t1(print_hello);
    std::thread t2(print_world);

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

    return 0;
}
  • t1 和 t2 是两个不同的线程,它们分别执行 print_hello 和 print_world 函数。
  • join() 确保在程序结束之前,所有线程都完成。

线程的分离

除了 join,还有 detach 函数。使用 detach 后,线程将独立于主线程执行,它不会被等待完成,主线程可以继续执行。

cpp 复制代码
#include <iostream>
#include <thread>

void print_message() {
    std::cout << "Message from detached thread!" << std::endl;
}

int main() {
    std::thread t(print_message);
    t.detach();  // 分离线程,主线程继续执行
    std::cout << "Main thread continues!" << std::endl;

    // 注意:如果主线程结束,分离线程可能未执行完成
    return 0;
}

注意:

  • 使用 detach 后,线程在后台执行,主线程可以退出,不会等待它的完成。若主线程结束时子线程仍在运行,则可能会出现未定义的行为,因此要小心使用 detach。

线程同步

多线程编程常常面临多个线程共享资源的情况。如果多个线程同时访问和修改共享数据,就会引发数据竞争问题。这时,需要对共享资源进行同步。

互斥量(Mutex)

互斥量是保护共享资源的一种机制,它确保同一时刻只有一个线程能访问共享资源。

cpp 复制代码
#include <iostream>
#include <thread>
#include <mutex>

std::mutex mtx;  // 互斥量

void print_message(const std::string &message) {
    mtx.lock();  // 上锁
    std::cout << message << std::endl;
    mtx.unlock();  // 解锁
}

int main() {
    std::thread t1(print_message, "Hello from thread 1");
    std::thread t2(print_message, "Hello from thread 2");

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

    return 0;
}
  • std::mutex mtx 定义了一个互斥量,用来保护共享资源(此处是 std::cout)。
  • mtx.lock() 获取锁,确保当前线程独占对 cout 的访问。
  • mtx.unlock() 释放锁,允许其他线程获取锁。

std::lock_guard 和 std::unique_lock

为了避免手动调用 lock() 和 unlock(),C++ 提供了 std::lock_guard 和 std::unique_lock,它们在作用域结束时自动释放锁,避免忘记解锁。

cpp 复制代码
#include <iostream>
#include <thread>
#include <mutex>

std::mutex mtx;  // 互斥量

void print_message(const std::string &message) {
    std::lock_guard<std::mutex> lock(mtx);  // 上锁
    std::cout << message << std::endl;
    // 自动解锁,当 lock_guard 对象超出作用域时
}

int main() {
    std::thread t1(print_message, "Hello from thread 1");
    std::thread t2(print_message, "Hello from thread 2");

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

    return 0;
}
  • std::lock_guard 自动获取和释放锁,使代码更加简洁安全。

std::condition_variable

std::condition_variable 用于在多个线程之间同步,通常用于实现线程之间的等待与通知机制。

cpp 复制代码
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>

std::mutex mtx;
std::condition_variable cv;
bool ready = false;

void print_id(int id) {
    std::unique_lock<std::mutex> lck(mtx);
    while (!ready) cv.wait(lck);  // 等待条件满足
    std::cout << "Thread " << id << std::endl;
}

void go() {
    std::unique_lock<std::mutex> lck(mtx);
    ready = true;  // 改变条件
    cv.notify_all();  // 通知所有线程
}

int main() {
    std::thread threads[10];
    for (int i = 0; i < 10; ++i) 
        threads[i] = std::thread(print_id, i);

    std::cout << "Waiting for threads to be ready..." << std::endl;
    go();  // 通知线程

    for (auto& th : threads) th.join();
    return 0;
}
  • cv.wait(lck) 会使当前线程等待直到 ready 为 true。
  • cv.notify_all() 会通知所有等待的线程继续执行。

原子操作

原子操作是不会被中断的操作,它对于多线程同步非常有用。在 C++11 中,std::atomic 提供了对基本数据类型的原子操作支持。

cpp 复制代码
#include <iostream>
#include <thread>
#include <atomic>

std::atomic<int> counter(0);

void increment() {
    for (int i = 0; i < 1000; ++i)
        ++counter;  // 原子增加
}

int main() {
    std::thread t1(increment);
    std::thread t2(increment);

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

    std::cout << "Final counter value: " << counter.load() << std::endl;
    return 0;
}
  • std::atomic 确保对 counter 的操作是原子的,不会被多个线程同时修改,避免了数据竞争。
相关推荐
一人の梅雨4 分钟前
西域平台关键字搜索接口开发指南
java·开发语言·数据库
Code_Geo8 分钟前
pyproj 库中 Geod 类讲解
开发语言·python·geod·pyproj
SummerGao.11 分钟前
CentOS 7 源码安装libjsoncpp-1.9.5库
linux·运维·centos·jsoncpp
SummerGao.17 分钟前
【解决】Linux命令报错:Cannot find a valid baseurl for repo: centos-sclo-rh/x86_64
linux·运维·centos
钢铁男儿20 分钟前
Python 序列构成的数组(对序列使用+和_)
服务器·windows·python
进取星辰21 分钟前
PyTorch 深度学习实战(28):对比学习(Contrastive Learning)与自监督表示学习
人工智能·深度学习
阿珊和她的猫25 分钟前
AIGC 与 Agentic AI:生成式智能与代理式智能的技术分野与协同演进
人工智能·aigc
飞凌嵌入式26 分钟前
从DeepSeek到Qwen,AI大模型的移植与交互实战指南
人工智能·aigc·嵌入式
不吃香菜?27 分钟前
OpenCV图像处理基础到进阶之高阶操作
图像处理·人工智能·opencv
豆芽81927 分钟前
基于Web的交互式智能成绩管理系统设计
前端·python·信息可视化·数据分析·交互·web·数据可视化