C++11 引入了 <thread> 头文件,为多线程编程提供了直接的支持。利用 <thread>,程序员可以在程序中创建和管理线程,实现并发执行,充分利用多核处理器的优势。本文介绍了 <thread> 的基本用法、线程创建与管理、以及常见的注意事项。

目录

  1. <thread> 头文件简介
  2. 线程的创建与启动
  3. 线程的管理
  4. 线程安全与同步
  5. 常见注意事项
  6. 结论

1. <thread> 头文件简介

<thread> 是 C++ 标准库中用于多线程编程的头文件,提供了 std::thread 类,用于创建和管理线程。借助 <thread>,可以启动独立执行的函数或可调用对象,使得程序能够并发运行多个任务。
要使用多线程功能,只需包含头文件:

#include <thread>


2. 线程的创建与启动

2.1 基本线程创建

可以通过 std::thread 创建一个线程,并将一个函数传递给它,使该函数在新线程中执行。

示例:

#include <iostream>
#include <thread>

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

int main() {
    // 创建一个线程,执行 threadFunction
    std::thread t(threadFunction);
    
    // 主线程等待子线程执行完毕
    t.join();
    
    std::cout << "Thread finished." << std::endl;
    return 0;
}

在上述代码中,线程 t 启动后会调用 threadFunction()。调用 t.join() 可等待子线程执行完毕,再继续主线程的执行。

2.2 传递参数给线程

可以在创建线程时向可调用对象传递参数。参数会被拷贝(或移动)传递到新线程中。

示例:

#include <iostream>
#include <thread>

void printMessage(const std::string &message, int count) {
    for (int i = 0; i < count; ++i) {
        std::cout << message << std::endl;
    }
}

int main() {
    // 向线程传递参数
    std::thread t(printMessage, "Hello, C++ multithreading!", 3);
    
    t.join();
    
    return 0;
}

在此示例中,字符串和整数参数被传递给线程函数 printMessage,线程会输出指定次数的消息。


3. 线程的管理

3.1 线程加入与分离

  • 加入(join): 调用 join() 后,主线程等待子线程结束再继续执行,确保线程之间的同步。
  • 分离(detach): 调用 detach() 后,线程在后台独立运行,与主线程分离。需要确保被分离线程在其生命周期内不会访问已销毁的资源。

示例:

#include <iostream>
#include <thread>
#include <chrono>

void backgroundTask() {
    std::this_thread::sleep_for(std::chrono::seconds(2));
    std::cout << "Background task completed." << std::endl;
}

int main() {
    std::thread t(backgroundTask);
    
    // 使用 detach() 分离线程,线程在后台运行
    t.detach();
    
    // 主线程继续执行其他任务
    std::cout << "Main thread is doing other work." << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(3));
    std::cout << "Main thread finished." << std::endl;
    
    return 0;
}

调用 detach() 后,线程 t 将不再受主线程控制。注意:分离线程后要确保它所依赖的资源在其运行期间有效。


4. 线程安全与同步

多线程环境下,多个线程同时访问共享资源时可能会发生竞态条件。为确保线程安全,常用互斥量(mutex)和锁(lock)。

4.1 互斥量(mutex)

std::mutex 用于保护共享数据,防止同时访问引发数据竞争。

示例:

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

std::mutex mtx;
int counter = 0;

void increment() {
    // 锁住互斥量,保护共享数据
    std::lock_guard<std::mutex> lock(mtx);
    ++counter;
    std::cout << "Counter: " << counter << std::endl;
}

int main() {
    std::thread t1(increment);
    std::thread t2(increment);
    
    t1.join();
    t2.join();
    
    return 0;
}

在上述代码中,std::lock_guard 自动管理互斥量的加锁和解锁,确保多个线程安全地更新 counter

4.2 锁(lock)

除了 std::lock_guard,还可以使用 std::unique_lock 来获得更灵活的锁管理功能,如延时加锁或条件变量协作。


5. 常见注意事项

  • 资源管理: 避免线程在退出前访问已销毁的资源。对于分离线程,需要确保其运行期间所需资源保持有效。
  • 异常处理: 在线程函数中捕获异常,防止未捕获异常导致程序崩溃。
  • 避免死锁: 多个互斥量同时使用时,应遵循一致的加锁顺序,避免死锁的发生。
  • 合理调度: 利用 std::this_thread::yield()sleep_for 进行适当的调度,让线程更公平地共享 CPU 时间。

6. 结论

C++ <thread> 提供了创建、管理和同步线程的基础设施,使得编写并发程序变得更简单和安全。通过掌握线程的基本用法、正确使用互斥量和锁等同步机制,你可以构建高效且线程安全的多线程应用程序。掌握 <thread> 是现代 C++ 编程的重要技能之一。

推荐阅读: