目 录CONTENT

文章目录

C++ 多线程:深入理解 std::thread

TalentQ
2025-07-24 / 0 评论 / 0 点赞 / 9 阅读 / 0 字

引言

在 C++11 之前,多线程基本依赖平台相关 API(POSIX pthread、Windows CreateThread)。

std::threadC++11 及以后 提供的跨平台线程抽象,特点:

  • 零成本抽象:内部直接映射 pthread / WinThread

  • RAII 语义:必须显式 joindetach,否则析构 terminate()

  • futureasyncchrono 无缝配合

1. 基础知识

1.1 什么是线程?

线程是操作系统能够进行运算调度的最小单位。多线程允许程序同时完成多个任务,提高程序的并发性和响应性。

1.2 C++11之前的多线程

在C++11之前,开发者常用如 pthreads(POSIX 线程)或操作系统特定的API实现多线程,非常繁琐且可移植性差。

1.3 C++11的std::thread

std::thread 封装了线程的创建与管理,极大简化了多线程编程。

2. std::thread 的基本用法

2.1 创建线程

可以通过传递 函数指针、函数对象、lambda表达式、非静态成员函数或静态成员函数 来创建线程。

#include <iostream>
#include <thread>

void PrintHello() {
  std::count << "Hello from thread!" << std::endl;
}

int main() {
  std::thread t(PrintHello);  // 创建线程并启动
  t.join();  // 等待线程结束、
  return 0;
}

2.2 传递参数

参数会被拷贝到新线程中。

#include <iostream>
#include <thread>

void PrintNum(int num) {
  std::cout << "Number: " << num << std::endl;
}

int main() {
  int n = 42;
  std::thread t(PrintNum, n);  // 传递参数
  t.join();
  return 0;
}

2.3 传递引用参数

需要使用 std::ref 包装引用参数。

#include <iostream>
#include <thread>

void Increment(int &num) {
  ++num;
}

int main() {
  int value = 0;
  std::thread t(Increment, std::ref(value));
  t.join();
  std::count << "Value: " << value << std::endl;
  return 0;
}

2.3 使用Lambda表达式

#include <iostream>
#include <thread>

int main() {
  std::thread t([]() {
    std::count << "Hello from lambda thread!" << std::endl;
  });
  t.join();
  return 0;
}

3. 线程的生命周期管理

3.1 join() 和 detach()

join():主线程等待子线程执行完毕。

detach():线程与主线程分离,后台运行。

#include <iosteam>
#include <thread>

void Task() {
  std::count << "Detached thread running!" << std::endl;
}

int main() {
  std::thread t(Task);
  t.detach();  // 不再受主线程管理
  std::this_thread::sleep_for(std::chrono::milliseconds(100));
  return 0;
}

注意:detach() 后线程必须自行管理资源,否则可能导致资源泄漏。

3.2 线程的可联结性

可以通过 joinable() 方法判断线程是否可以 join()detach()

std::thread t(Task);
if (t.joinable()) {
  t.join();
}

4. 线程安全与数据同步

多线程访问共享数据时,必须保证线程安全。C++11提供了多种同步机制。下面是一个简单的代码示例:

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

std::mutex g_mutex;
int g_counter = 0;

void SafeIncrement() {
  std::lock_guard<std::mutex> lock(g_mutex);
  ++g_counter;
}

int main() {
  std::thread t1(SafeIncrement);
  std::thread t2(SafeIncrement);
  t1.join();
  t2.join();
  std::cout << "Counter: " << g_counter << std::endl;
  return 0;
}

5. 高级用法

5.1 移动线程

线程对象不可拷贝,但是可以移动。

std::thread t1(Task);
std::thread t2 = std::move(t1);  // t1 变为不可用

5.2 硬件并发数量

使用 std::thread::hardware_concurrency() 获取建议的线程数。

unsigned int n = std::thread::hardware_concurrency();
std::cout << "Hardware concurrency: " << n << std::endl;

5.3 线程ID

每个线程有唯一的ID,可通过 get_id() 获取。

std::cout << "Thread ID: " << std::this_thread::get_id() << std::endl;

6. 注意事项

  • 每个线程对象必须在销毁前 join() detach() ,否则程序会异常终止。

  • 避免数据竞争,合理使用锁。

  • 尽量减少锁的粒度,防止性能瓶颈。

  • 推荐使用RAII(如 std::lock_guard)锁管理器,防止忘记释放。

0

评论区