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 的操作是原子的,不会被多个线程同时修改,避免了数据竞争。
相关推荐
大刚测试开发实战5 小时前
TestHub V0.2.2版本发布,附更新指南
人工智能
冬奇Lab6 小时前
Agent 系列(21):Harness 测试工程——45 个测试怎么设计,以及它发现了什么 bug
人工智能·llm·agent
冬奇Lab6 小时前
每日一个开源项目(第133篇):EchoBird - 把 AI 工具的安装和部署做成傻瓜操作
人工智能·开源·资讯
程序员龙叔7 小时前
编写高质量 Skill 系列 -- 如何设计需求分析与用例生成的 SKILL
自动化测试·软件测试·python·软件测试工程师·接口测试·性能测试·skill·ai测试
_wyt0017 小时前
洛谷 B3930 [GESP202312 五级] 烹饪问题 题解
c++·gesp
IT_陈寒8 小时前
Redis的SETNX并发问题让我加了三天班
前端·人工智能·后端
用户5191495848459 小时前
Windows 渗透测试载荷加载器 POC 工具集
人工智能·aigc
大树889 小时前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
用户8356290780519 小时前
使用 Python 操作 Word 内容控件
后端·python
摇滚侠9 小时前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql