传统的C++(C++11标准之前)中并没有引入线程这个概念,在C++11出来之前,如果想要在C++中实现多线程,需要借助操作系统平台提供的API,比如Linux的<pthread.h>,或者windows下的<windows.h> 。 C++11提供了语言层面上的多线程,包含在头文件中,它解决了跨平台的问题,提供了管理线程、保护共享数据、线程间同步操作、原子操作等类。 |
一、线程管理(std::thread)
std::thread 是 C++ 标准库中用于创建和管理线程的类。 |
#include<thread> |
1、构造函数
|
2、成员函数
|
3、静态成员函数
hardware_concurrency() noexcept: 返回可以并发执行的线程数量的估计。 |
4、全局函数
|
5、特殊成员函数
|
6、示例代码
// 构造函数
#include <iostream>
#include <thread>
// 线程函数,接受一个整数参数并打印
void printNumber(int num) {
std::cout << "Printing number: " << num << std::endl;
}
int main() {
// 创建一个线程,并传递参数
int num = 42;
std::thread t(printNumber, num);
// 等待线程执行完毕
t.join();
std::cout << "Thread finished execution." << std::endl;
return 0;
}
// 赋值操作
#include <stdio.h>
#include <stdlib.h>
#include <chrono> // std::chrono::seconds
#include <iostream> // std::cout
#include <thread> // std::thread, std::this_thread::sleep_for
void thread_task(int n) {
std::this_thread::sleep_for(std::chrono::seconds(n));
std::cout << "hello thread "
<< std::this_thread::get_id()
<< " paused " << n << " seconds" << std::endl;
}
int main(int argc, const char *argv[])
{
std::thread threads[5];
std::cout << "Spawning 5 threads...\n";
for (int i = 0; i < 5; i++) {
threads[i] = std::thread(thread_task, i + 1);
}
std::cout << "Done spawning threads! Now wait for them to join\n";
for (auto& t: threads) {
t.join();
}
std::cout << "All threads joined.\n";
return EXIT_SUCCESS;
}
// 等待进程退出(主线程退出时要等待所有子线程退出,确保线程资源在程序结束时得到正确释放)
if (formatThread.joinable()) {
abortCreateEncfs();
formatThread.join();
}
// 单次定时器
class SimpleTimer {
public:
SimpleTimer() : running(false) {}
void SetCallback(std::function<void()> callback) {
this->callback = callback;
}
void Start(int intervalMs) {
if (running) {
Stop();
}
running = true;
timerThread = std::thread([this, intervalMs]() {
while (running) {
auto start = std::chrono::steady_clock::now();
if (callback) {
callback();
}
std::this_thread::sleep_until(start + std::chrono::milliseconds(intervalMs));
}
});
}
void Stop() {
running = false;
if (timerThread.joinable()) {
timerThread.join();
}
}
~SimpleTimer() {
Stop();
}
private:
std::thread timerThread;
std::atomic<bool> running;
std::function<void()> callback;
};
二、线程延时
std::this_thread::sleep_for(std::chrono::duration); |
std::chrono 提供了不同的时间单位: std::chrono::seconds: 秒 std::chrono::milliseconds: 毫秒 std::chrono::microseconds: 微秒 std::chrono::nanoseconds: 纳秒 |
使用示例: 暂停 500 毫秒:std::this_thread::sleep_for(std::chrono::milliseconds(500)); 暂停 1 微秒:std::this_thread::sleep_for(std::chrono::microseconds(1)); |
三、线程同步
1、互斥量(std::mutex)
std::mutex 是 C++ 标准库中的一个类,用于实现互斥锁,保护共享资源,防止多个线程同时访问。 |
#include<mutex> |
# 构造函数: std::mutex() noexcept: 默认构造函数,创建一个未加锁的互斥量对象。 |
# 成员函数:
|
# RAII封装类:
|
|
#include <iostream>
#include <thread>
#include <mutex>
std::mutex mtx;
void printThreadSafe(const std::string& msg) {
// 使用 lock_guard 自动管理锁的生命周期
std::lock_guard<std::mutex> lock(mtx);
std::cout << msg << std::endl;
}
void tryLockExample() {
if (mtx.try_lock()) {
std::cout << "try_lock succeeded" << std::endl;
mtx.unlock();
} else {
std::cout << "try_lock failed" << std::endl;
}
}
int main() {
std::thread t1(printThreadSafe, "Thread 1: Hello, world!");
std::thread t2(printThreadSafe, "Thread 2: Hello, world!");
t1.join();
t2.join();
std::thread t3(tryLockExample);
t3.join();
return 0;
}
2、递归互斥量(std::recursive_mutex)
std::recursive_mutex 是 C++ 标准库中的一个类,用于实现递归互斥锁,它允许同一个线程多次锁定同一个互斥量,而不会导致死锁。这在递归函数或复杂的代码路径中需要多次进入同一个临界区时特别有用。 |
#include<mutex> |
# 构造函数 recursive_mutex() noexcept: 默认构造函数,创建一个未加锁的递归互斥量对象。 |
# 成员函数
|
# RAII封装类 与互斥量一致。 |
#include <iostream>
#include <thread>
#include <mutex>
std::recursive_mutex rec_mtx;
void recursiveFunction(int count) {
if (count <= 0) return;
// 使用 lock_guard 自动管理锁的生命周期
std::lock_guard<std::recursive_mutex> lock(rec_mtx);
std::cout << "Recursive call count: " << count << std::endl;
// 递归调用
recursiveFunction(count - 1);
}
void tryLockExample() {
if (rec_mtx.try_lock()) {
std::cout << "try_lock succeeded" << std::endl;
rec_mtx.unlock();
} else {
std::cout << "try_lock failed" << std::endl;
}
}
int main() {
// 创建一个线程并调用递归函数
std::thread t1(recursiveFunction, 5);
t1.join();
// 使用 try_lock 示例
std::thread t2(tryLockExample);
t2.join();
return 0;
}
3、条件变量(std::condition_variable)
std::condition_variable 是 C++ 标准库中的一个类,用于实现线程间的同步和通信,允许一个线程等待特定条件,并被其他线程通知条件满足。 |
#include<condition_variable> |
# 构造函数
|
# 成员函数
|
#include <iostream>
#include <vector>
#include <thread>
#include <queue>
#include <future>
#include <functional>
#include <condition_variable>
class ThreadPool {
public:
ThreadPool(size_t numThreads);
~ThreadPool();
template <class F, class... Args>
auto enqueue(F&& f, Args&&... args) -> std::future<typename std::result_of<F(Args...)>::type>;
private:
std::vector<std::thread> workers; // 工作线程
std::queue<std::function<void()>> tasks; // 任务队列
std::mutex queueMutex; // 任务队列的互斥锁
std::condition_variable condition; // 条件变量,用于通知工作线程有任务可执行
bool stop; // 标记线程池是否停止
};
// 构造函数:创建指定数量的线程,并让它们等待任务
ThreadPool::ThreadPool(size_t numThreads) : stop(false) {
for (size_t i = 0; i < numThreads; ++i) {
workers.emplace_back([this] {
while (true) {
std::unique_lock<std::mutex> lock(this->queueMutex);
// 如果任务队列为空则等待并释放互斥量queueMutex
this->condition.wait(lock, [this] { return this->stop || !this->tasks.empty(); });
if (this->stop && this->tasks.empty()) return;
std::function<void()> task = std::move(this->tasks.front());
this->tasks.pop();
task(); // 执行任务
}
});
}
}
// 向线程池提交任务
template <class F, class... Args>
auto ThreadPool::enqueue(F&& f, Args&&... args) -> std::future<typename std::result_of<F(Args...)>::type> {
using return_type = typename std::result_of<F(Args...)>::type;
auto task = std::make_shared<std::packaged_task<return_type()>>(std::bind(std::forward<F>(f), std::forward<Args>(args)...));
std::future<return_type> res = task->get_future();
{
std::unique_lock<std::mutex> lock(queueMutex);
tasks.emplace([task]() { (*task)(); });
}
condition.notify_one(); // 通知工作线程有任务可执行
return res;
}
// 析构函数:停止所有工作线程
ThreadPool::~ThreadPool() {
{
std::unique_lock<std::mutex> lock(queueMutex);
stop = true;
}
condition.notify_all();
for (std::thread& worker : workers) worker.join();
}
int main() {
ThreadPool pool(4);
auto result = pool.enqueue([](int a, int b) { return a + b; }, 5, 10);
std::cout << "Result: " << result.get() << std::endl; // 输出15
return 0;
}
4、自旋锁(atomic)
自旋锁指的是当一个线程在获取锁的时候,如果锁已经被其他线程所获取,那么该线程就将进入一个循环,不断的去判断自身是否能够获得锁 ,直到该线程获得了锁,线程才会退出循环。 自旋锁与互斥锁一样是一个为了实现对共享资源的保护而提出的锁机制,都是为了解决对某项资源的互斥使用,它们保证了在并发过程中,该共享资源在任意一个时间端都只有一个拥有者,但是与互斥锁不同的是,自旋锁在共享资源已经被占用的情况下,该线程不会进入阻塞状态,如果自旋锁已经被其他线程所占有,此时试图调用自旋锁将进入循环状态来查看是否能不能获得该锁,而这本身也是由于两者在调度机制上有所不同所造成的。 注意: 未获取锁的线程一直没有休眠处于活跃状态,虽然它本身并不执行什么工作,但是它依旧会消耗cpu,称这种状态叫busy waitting。 |
在多线程编程中,多个线程可能会同时访问和修改共享数据,如果没有适当的同步机制,线程之间的干扰会导致数据竞争和不一致性,原子操作通过硬件或软件保证某个操作在执行时不会被打断,从而防止数据竞争; 在C++中,std::atomic提供了一种便捷且高效的方式来实现原子操作,以确保在多线程环境下对共享变量的操作是线程安全的,可以使用std::atomic来实现锁、计数器、标志等功能; 原子操作是多线程编程中的重要工具,提供了一种无锁的方式来实现线程安全,通过理解和正确使用原子操作,可以编写出高效且安全的多线程代码,C++中的std::atomic类型和相关操作使得原子操作的使用变得更加方便和高效。 |
# 原子类型:
|
加载和存储: |
load():从原子变量读取值 store():向原子变量写入值 |
交换操作: |
exchange():原子地设置变量新值,并返回旧值 |
比较并交换(CAS): |
compare_exchange_weak():原子地比较并交换,允许弱比较 compare_exchange_strong():原子地比较并交换,强比较 |
算术操作: |
fetch_add():原子地加法操作 fetch_sub():原子地减法操作 fetch_and():原子地按位与操作 fetch_or():原子地按位或操作 |
// 基于原子操作的锁(CAS锁),在两个线程中,每个线程尝试对共享变量 sum 进行操作,同时通过 CAS 类的 lock 和 unlock 方法来确保操作的原子性和线程安全。
#include <iostream>
#include <atomic>
#include <thread>
using namespace std;
class CAS {
private:
std::atomic<bool> flag;
public:
CAS():flag(false) {}
CAS(const CAS&)=delete;
CAS& operator =(const CAS&)=delete;
void lock() {
bool expect = false;
// 判断能否获得锁
while(!flag.compare_exchange_strong(expect, true)) {
// 失败后,再次尝试
expect = false;
}
}
void unlock() {
// 解锁
flag.store(false);
}
};
int sum = 0;
CAS cas;
void fun()
{
for(int i = 0;i < 10;i++) {
cas.lock();
cout << "sum:" << sum++ << endl;
cas.unlock();
}
}
int main()
{
std::thread t1(fun);
std::thread t2(fun);
t1.join();
t2.join();
return 0;
}
四、异步处理
c++11还提供了异步接口std::async,通过这个异步接口可以很方便的获取线程函数的执行结果,std::async会自动创建一个线程去调用 线程函数,它返回一个std::future,这个future中存储了线程函数返回的结果,当需要线程函数的结果时,直接从future中获取。 |
#include <future> #include <thread> |
1、std::async/std::packaged_task
async(std::launch::async | std::launch::deferred, f, args...) |
std::launch::async:在调用async就开始创建线程; f:线程函数; args:线程函数的参数。 |
template <typename R, typename... Args>class packaged_task; |
R 是可调用对象的返回类型; Args... 是可调用对象的参数类型。 |
2、std::future
std::future 是与异步任务关联对象,用于获取异步操作结果,检查任务是否完成,或者处理任务中的异常。 |
get():阻塞并获取任务的结果。如果任务抛出异常,get() 会重新抛出该异常; wait():阻塞直到任务完成,但不返回结果。用于等待任务完成; wait_for():等待任务直到指定时间(时间间隔),若任务在此时间内未完成,则返回; wait_until():类似于 wait_for,但是等待的是一个具体的时间点。 |
3、std::status
std::future_status 是 C++ 标准库中用于描述 std::future 对象的当前状态的一个枚举类型,用于判断异步任务的执行状态,可以用来检查异步操作是否完成、是否超时等。 |
|
4、示例代码
std::async: |
#include <iostream> #include <future> #include <thread> #include <chrono> int main() { // 示例 1:简单的返回值 std::future<int> f1 = std::async(std::launch::async, []() { return 8; }); std::cout << "f1 result: " << f1.get() << std::endl; // 输出: 8 // 示例 2:无返回值的异步任务 std::future<void> f2 = std::async(std::launch::async, []() { std::cout << "f2 prints: 8" << std::endl; // 输出: 8 }); f2.wait(); // 等待 f2 完成 // 示例 3:带有延时的异步任务 std::future<int> future = std::async(std::launch::async, []() { std::this_thread::sleep_for(std::chrono::seconds(3)); // 模拟任务延时 return 8; // 返回值 8 }); std::cout << "waiting...\n"; // 主线程等待 std::future_status status; // 使用 while 循环不断检查任务的状态 do { status = future.wait_for(std::chrono::seconds(1)); // 每秒检查一次 if (status == std::future_status::deferred) { std::cout << "Status: deferred\n"; // 如果任务被延迟 } else if (status == std::future_status::timeout) { std::cout << "Status: timeout (still waiting)\n"; // 如果任务超时未完成 } else if (status == std::future_status::ready) { std::cout << "Status: ready (task completed)\n"; // 如果任务已完成 } } while (status != std::future_status::ready); // 直到任务完成 // 获取任务的结果并输出 std::cout << "Result: " << future.get() << '\n'; // 输出任务的返回值: 8 return 0; } |
std::packaged_task: |
#include <iostream> #include <thread> #include <future> std::string promise_string(std::string str) { for (int i = 0; i < 100; i++) { std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::cout << "sleep" << std::endl; } std::string str1 = "the current function name is "; str1.append(__FUNCTION__); return str + str1; } int main() { std::packaged_task<std::string(std::string)> pTask(promise_string); std::future<std::string> fu = pTask.get_future(); std::thread tr(std::move(pTask),"测试 "); std::string strR = fu.get(); std::cout << "the result is:" << strR.c_str() << std::endl; tr.join(); system("pause"); } |
五、线程池
传统的多线程编程中,为每个任务创建和销毁线程时,这会引入较高的开销,因为线程创建、销毁和上下文切换都是昂贵的操作; 线程池(Thread Pool)是一种线程管理机制,主要用于限制系统中线程的数量、重用线程资源以及减少线程频繁创建和销毁的开销,通过维护一个线程集合(通常是固定数量的工作线程),让这些线程来执行任务而不必为每个任务都创建一个新线程,适用于频繁创建/销毁异步任务。 线程池的核心组件:
|
#include <iostream>
#include <vector>
#include <thread>
#include <queue>
#include <future>
#include <functional>
#include <condition_variable>
class ThreadPool {
public:
ThreadPool(size_t numThreads);
~ThreadPool();
template <class F, class... Args>
auto enqueue(F&& f, Args&&... args) -> std::future<typename std::result_of<F(Args...)>::type>;
private:
std::vector<std::thread> workers; // 工作线程
std::queue<std::function<void()>> tasks; // 任务队列
std::mutex queueMutex; // 任务队列的互斥锁
std::condition_variable condition; // 条件变量,用于通知工作线程有任务可执行
bool stop; // 标记线程池是否停止
};
// 构造函数:创建指定数量的线程,并让它们等待任务
ThreadPool::ThreadPool(size_t numThreads) : stop(false) {
for (size_t i = 0; i < numThreads; ++i) {
workers.emplace_back([this] {
while (true) {
std::unique_lock<std::mutex> lock(this->queueMutex);
this->condition.wait(lock, [this] { return this->stop || !this->tasks.empty(); });
if (this->stop && this->tasks.empty()) return;
std::function<void()> task = std::move(this->tasks.front());
this->tasks.pop();
// 释放任务队列锁 - 防止阻塞其他任务导致顺序执行
lock.unlock();
task(); // 执行任务
}
});
}
}
// 向线程池提交任务
template <class F, class... Args>
auto ThreadPool::enqueue(F&& f, Args&&... args) -> std::future<typename std::result_of<F(Args...)>::type> {
using return_type = typename std::result_of<F(Args...)>::type;
auto task = std::make_shared<std::packaged_task<return_type()>>(std::bind(std::forward<F>(f), std::forward<Args>(args)...));
std::future<return_type> res = task->get_future();
{
std::unique_lock<std::mutex> lock(queueMutex);
tasks.emplace([task]() { (*task)(); });
}
condition.notify_one(); // 通知工作线程有任务可执行
return res;
}
// 析构函数:停止所有工作线程
ThreadPool::~ThreadPool() {
{
std::unique_lock<std::mutex> lock(queueMutex);
stop = true;
}
condition.notify_all();
for (std::thread& worker : workers) worker.join();
}
int main() {
ThreadPool pool(4);
auto result = pool.enqueue([](int a, int b) { return a + b; }, 5, 10);
std::cout << "Result: " << result.get() << std::endl; // 输出15
return 0;
}