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 的操作是原子的,不会被多个线程同时修改,避免了数据竞争。
相关推荐
码上淘金38 分钟前
【Python】Python常用控制结构详解:条件判断、遍历与循环控制
开发语言·python
Brilliant Nemo40 分钟前
四、SpringMVC实战:构建高效表述层框架
开发语言·python
孙克旭_1 小时前
PXE_Kickstart_无人值守自动化安装系统
linux·运维·自动化
2301_787552871 小时前
console-chat-gpt开源程序是用于 AI Chat API 的 Python CLI
人工智能·python·gpt·开源·自动化
虾球xz1 小时前
游戏引擎学习第268天:合并调试链表与分组
c++·学习·链表·游戏引擎
懵逼的小黑子1 小时前
Django 项目的 models 目录中,__init__.py 文件的作用
后端·python·django
layneyao1 小时前
AI与自然语言处理(NLP):从BERT到GPT的演进
人工智能·自然语言处理·bert
皓月盈江2 小时前
Linux电脑本机使用小皮面板集成环境开发调试WEB项目
linux·php·web开发·phpstudy·小皮面板·集成环境·www.xp.cn
fpcc2 小时前
跟我学c++高级篇——模板元编程之十三处理逻辑
c++
Y3174292 小时前
Python Day23 学习
python·学习