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 的操作是原子的,不会被多个线程同时修改,避免了数据竞争。
相关推荐
刘卜卜&嵌入式32 分钟前
C++_设计模式_观察者模式(Observer Pattern)
c++·观察者模式·设计模式
_一条咸鱼_1 小时前
Python 数据类型之可变与不可变类型详解(十)
人工智能·python·面试
_一条咸鱼_1 小时前
Python 入门之基本运算符(六)
python·深度学习·面试
_一条咸鱼_1 小时前
Python 语法入门之基本数据类型(四)
人工智能·深度学习·面试
2201_754918411 小时前
卷积神经网络--手写数字识别
人工智能·神经网络·cnn
_一条咸鱼_1 小时前
Python 用户交互与格式化输出(五)
人工智能·深度学习·面试
h汉堡1 小时前
C++入门基础
开发语言·c++·学习
_一条咸鱼_1 小时前
Python 流程控制之 for 循环(九)
人工智能·python·面试
_一条咸鱼_1 小时前
Python 语法入门之流程控制 if 判断(七)
人工智能·python·面试
_一条咸鱼_1 小时前
Python 流程控制之 while 循环(八)
人工智能·python·面试