C++11/14/17多线程和线程池(万字)
01 C++多线程基础
为什么要用多线程
多线程(Multithreading) 是指在一个进程(程序的运行实例)中同时创建和运行多个线程,这些线程共享进程的内存空间(如全局变量、堆内存),但拥有独立的执行路径(程序计数器、栈空间)。简单来说,多线程允许程序 “同时” 执行多个任务。
多线程的核心价值如下:
(1)提高CPU使用率:对于多核CPU,单线程程序只能利用一个核心,其他核心处于空闲状态
(2)实现 “并发响应”:多线程可将耗时操作放在后台线程,主线程保持响应。
(3)拆分复杂任务:多线程可按功能拆分任务,每个线程负责一部分,简化代码逻辑。
std::thread对象
#include <thread>
#include <iostream>
using namespace std;using namespace this_thread;
void threadFunction(int id) { //参数 id:用于标识线程(区分不同线程)。
cout << "Thread " << id << " is running." << endl;
}
int main() {cout << "main thread id: " << get_id() << endl;
const int numThreads = 3;
thread threads[numThreads];//存储3个thread对象
// Create and start threads
for (int i = 0; i < numThreads; ++i) {
//通过 std::thread 的构造函数创建线程,并传递线程函数和参数
//第一个参数是线程函数 threadFunction,第二个参数是线程的标识 id
threads[i] = thread(threadFunction, i);
}
for (int i = 0; i < numThreads; ++i) {
threads[i].join(); //阻塞主线程,等待所有线程完成(join() 函数)
}
cout << "All threads have finished." << endl;
return 0;
}
注:由于没有线程同步机制,此代码线程每次执行输出顺序是随机的
std::thread 对象的生命周期阶段:
1. 创建阶段(初始化)
当 std::thread 对象被构造时(如 std::thread t(func)),其生命周期开始。
2. 活跃阶段(管理线程)
std::thread 对象创建后(非空状态),进入活跃阶段,此时它唯一关联一个正在运行的线程。
3. 销毁阶段(析构)
当 std::thread 对象离开作用域(如函数返回)或被显式销毁时,析构函数调用,生命周期结束。
- 若调用
join():线程执行体完成后,std::thread对象进入空状态,对象销毁不影响线程(已结束)。 - 若调用
detach():std::thread对象销毁后,线程执行体仍可继续运行(直到完成),但无法再通过对象控制线程。
int main() { std::thread t(func); if (t.joinable()) { // 检查是否处于活跃状态 t.join(); // 等待线程执行完毕,t 进入空状态 } return 0; // t 析构时安全 }int main() { std::thread t(func); if (t.joinable()) { t.detach(); // 线程与对象分离,t 进入空状态 } return 0; // t 析构安全,线程在后台继续运行直到完成 }
线程函数传入类对象
#include <thread>
#include <iostream>
using namespace std;
using namespace this_thread;
class Person {
public:
int age;
Person(int age) : age(age) { cout << "person's age is" << age << endl; }
Person(const Person& p) : age(p.age) { cout << "copy person's age is" << age << endl; }
~Person() { cout << "person's age " << age << " is destroyed" << endl; }
};void threadFunction(int id,Person p) { //参数 id:用于标识线程(区分不同线程)。
cout << "Thread " << id << " is running." << endl;
cout << "Thread person's age is " << p.age<< endl;
}int main() {
thread th;
Person p(30);
th = thread(threadFunction, 1, p);
th.join(); //阻塞主线程,等待所有线程完成(join() 函数)
cout << "All threads have finished." << endl;
return 0;
}
输入结果如下:
person's age is30 //主线程中创建的
copy person's age is30 //p 作为参数传递给线程函数时,复制到thread对象内部的临时存储区
copy person's age is30 //临时存储区的副本再次复制到线程函数的参数p
Thread 1 is running.
Thread person's age is 30
person's age 30 is destroyed //线程栈内的参数对象(第二次复制的副本)被销毁
person's age 30 is destroyed //临时存储区对象在线程结束后被销毁
All threads have finished.
person's age 30 is destroyed //主线程执行到return 0时,原始对象p超出作用域被销毁
注:若要避免多余的复制,可通过传递引用(需用std::ref包装,且确保原始对象生命周期长于线程)改一行th = thread(threadFunction, 1, ref(p)) 结果如下
person's age is30
copy person's age is30
Thread 1 is running.
Thread person's age is 30
person's age 30 is destroyed
All threads have finished.
person's age 30 is destroyed
如果继续改一行threadFunction(int id, const Person& p) 结果如下
person's age is30
Thread 1 is running.
Thread person's age is 30
All threads have finished.
person's age 30 is destroyed
成员函数作为线程函数
成员函数作为线程函数时,需传递对象地址和函数地址
#include <thread>
#include <iostream>
using namespace std;
using namespace this_thread;class Person {
public:
int age;
Person(int age) : age(age) { cout << "person's age is" << age << endl; }
Person(const Person& p) : age(p.age) { cout << "copy person's age is" << age << endl; }
~Person() { cout << "person's age " << age << " is destroyed" << endl; }// 将线程函数改为成员函数(参数保留id,无需再传Person对象,直接访问this->age)
void threadFunction(int id) {
cout << "Thread " << id << " is running." << endl;
cout << "Thread person's age is " << this->age << endl; // 访问当前对象的age
}
};int main() {
thread th;
Person p(30); // 创建Person对象p// 关键:成员函数作为线程函数时,需传递对象地址和函数地址
th = thread(&Person::threadFunction, &p, 1); // &p是对象实例地址,1是函数参数th.join();
cout << "All threads have finished." << endl;
return 0;
}
lambda表达式作为线程入口函数
#include <thread>
#include <iostream>
using namespace std;
using namespace this_thread;class Person {
public:
int age;
Person(int age) : age(age) { cout << "person's age is" << age << endl; }
Person(const Person& p) : age(p.age) { cout << "copy person's age is" << age << endl; }
~Person() { cout << "person's age " << age << " is destroyed" << endl; }
};int main() {
thread th;
Person p(30); // 创建Person对象p// 用lambda表达式替代threadFunction,直接捕获p的引用
th = thread([&p](int id) { // [&]表示按引用捕获外部变量
cout << "Thread " << id << " is running." << endl;
cout << "Thread person's age is " << p.age << endl; // 直接访问p.age
}, 1); // 传递参数1(对应lambda的id)th.join();
cout << "All threads have finished." << endl;
return 0;
}
02 多线程的通信和同步
多线程的状态
线程的生命周期通常可分为5 种核心状态:
新建状态(New):std::thread对象已创建,但尚未启动(未调用线程函数)。
就绪状态(Runnable):线程已启动(线程函数开始执行),但未被操作系统调度到 CPU 执行。此时线程处于 “等待 CPU 时间片” 的状态
运行状态(Running):线程获得 CPU 时间片,正在执行线程函数中的代码。
阻塞状态(Blocked/Waiting):线程暂时停止执行,放弃 CPU 资源,不占用cpu资源
终止状态(Terminated):线程函数执行完毕,或被异常终止,线程生命周期结束。
竞争状态 、临界区和互斥锁
临界区是指程序中访问共享资源(如全局变量、堆内存、文件等)的代码片段,这些代码在多线程环境下若被同时执行,可能导致数据不一致或未定义行为。
竞争状态是指多个线程同时访问临界区(操作共享资源)时,由于线程调度顺序的不确定性,导致程序最终结果与预期不符的现象。
竞争状态的本质是:临界区代码的执行被线程调度器 “打断”,多个线程的操作相互干扰。
std::mutex mtx用于保护临界区,确保同一时间只有一个线程访问共享资源。
#include <iostream>
#include <thread>
#include <mutex>
#include <vector>// 共享资源:计数器
int shared_counter = 0;
// 互斥锁:保护共享资源
std::mutex mtx;// 线程函数:累加计数器(10000次)
void increment_counter(int thread_id) {
for (int i = 0; i < 10000; ++i) {
// 加锁:进入临界区前获取锁
std::lock_guard<std::mutex> lock(mtx);
// 临界区:操作共享资源
int old_value = shared_counter;
shared_counter = old_value + 1;
// 解锁:lock_guard离开作用域自动释放锁
}
std::cout << "线程" << thread_id << "执行完毕" << std::endl;
}int main() {
const int num_threads = 3;
std::vector<std::thread> threads;// 创建并启动线程
for (int i = 0; i < num_threads; ++i) {
threads.emplace_back(increment_counter, i);
}// 等待所有线程完成
for (auto& t : threads) {
t.join();
}
std::cout << "所有线程执行完毕,最终计数器值:" << shared_counter << std::endl;return 0;
}
std::mutex是 “锁本身”,负责实际的加锁和解锁逻辑;std::lock_guard是 “锁的管理者”,通过 RAII 机制自动调用mutex的lock()和unlock(),确保锁一定会被释放,避免死锁,是使用mutex的推荐方式。
超时锁、递归锁和共享锁
超时锁(Timeout-based Lock) 是一种特殊的锁机制,允许线程在尝试获取锁时设置一个超时时间。若在超时时间内成功获取锁,则继续执行临界区代码;若超过时间仍未获取到锁,则放弃等待并返回失败,避免线程永久阻塞(如死锁场景)。
C++ 标准库通过 std::timed_mutex 和 std::unique_lock 配合实现超时锁功能,核心是 try_lock_for(相对时间)和 try_lock_until(绝对时间)两个方法。
#include <iostream>
#include <thread>
#include <mutex>
#include <chrono> // 时间相关库// 超时互斥锁(支持超时获取)
std::timed_mutex timed_mtx;
int shared_resource = 0;// 线程函数:尝试在3秒内获取锁,成功则修改共享资源
void task(int thread_id) {
// unique_lock配合timed_mutex,支持超时操作
std::unique_lock<std::timed_mutex> lock(timed_mtx, std::defer_lock); // 延迟加锁// 尝试在3秒内获取锁(相对时间)
if (lock.try_lock_for(std::chrono::seconds(3))) {
// 成功获取锁,执行临界区操作
std::cout << "线程" << thread_id << ":成功获取锁,修改资源" << std::endl;
shared_resource++;
std::this_thread::sleep_for(std::chrono::seconds(4)); // 模拟耗时操作
// lock离开作用域时自动解锁
}
else {
// 超时未获取锁,执行备选逻辑
std::cout << "线程" << thread_id << ":超时未获取锁,放弃操作" << std::endl;
}
}int main() {
std::thread t1(task, 1);
std::thread t2(task, 2);t1.join();
t2.join();//由于超时锁是3s,一个线程需要4s,因此只有一个线程完成了shared_resource++
std::cout << "最终共享资源值:" << shared_resource << std::endl;
return 0;
}
注:std::unique_lock<std::timed_mutex> lock(timed_mtx, std::defer_lock);
lock是std::unique_lock 类型的对象实例,用于管理 timed_mtx
std::defer_lock是一个特殊的标记常量(定义在 <mutex> 中),作用是告诉 unique_lock:构造对象时不要立即加锁,锁的获取由后续手动操作控制。后续可通过 lock.try_lock_for()、lock.try_lock_until()手动控制加锁时机,尤其适合超时锁场景(需要指定超时时间获取锁)。
递归锁(Recursive Mutex) 是一种特殊的互斥锁,允许同一个线程多次获取同一把锁而不会导致死锁。普通互斥锁(std::mutex)若被同一线程重复获取,会立即引发死锁,而递归锁通过记录 “获取次数” 解决了这一问题,适用于递归函数或同一线程需多次进入临界区的场景。
#include <iostream>
#include <mutex>
#include <thread>// 递归锁(支持同一线程多次获取)
std::recursive_mutex rec_mtx;
void recursive_task(int depth) {
// 第一次获取锁(引用计数变为1)
std::lock_guard<std::recursive_mutex> lock(rec_mtx);std::cout << "递归深度:" << depth << ",当前线程持有锁" << std::endl;
// 递归终止条件
if (depth <= 1) {
return;
}// 递归调用:同一线程再次获取锁(引用计数变为2)
recursive_task(depth - 1);
}int main() {
// 启动线程执行递归任务
std::thread t(recursive_task, 3);
t.join();std::cout << "所有操作完成,锁已释放" << std::endl;
return 0;
}
共享锁:C++17 是共享锁标准化的重要版本,正式引入了 **std::shared_mutex**(定义在<shared_mutex>头文件中),并配套std::shared_lock。
- 独占锁模式:通过
lock()/unlock()获取 / 释放独占锁(写操作),同一时间仅允许一个线程持有,用于修改共享资源。 - 共享锁模式:通过
lock_shared()/unlock_shared()获取 / 释放共享锁(读操作),同一时间允许多个线程持有,用于读取共享资源。
std::shared_mutex rw_mutex; // 读写锁(C++17)
int shared_data = 0; // 共享资源// 读操作:获取共享锁(多线程可同时读)
void read_task(int id) {
std::shared_lock<std::shared_mutex> lock(rw_mutex); // 共享锁
std::cout << "线程" << id << " 读数据:" << shared_data << std::endl;
}// 写操作:获取独占锁(仅单线程可写)
void write_task(int id) {
std::unique_lock<std::shared_mutex> lock(rw_mutex); // 独占锁
shared_data++; // 修改共享资源
std::cout << "线程" << id << " 写数据:" << shared_data << std::endl;
}
注:代码中通过两种不同的锁管理工具(RAII 类)显式区分锁类型
std::shared_lock<std::shared_mutex>:专门用于管理共享锁,构造时自动获取共享锁,析构时自动释放。std::unique_lock<std::shared_mutex>:专门用于管理独占锁,构造时自动获取独占锁,析构时自动释放。
这两个工具的命名也直观体现了锁的特性:shared 对应 “可共享”(多线程同时持有),unique 对应 “独占”(仅单线程持有)。
03 锁管理工具RAII
什么是RAII
局部对象(在函数内或代码块内定义的对象)一旦超出作用域就会被释放(出栈)
RAII(Resource Acquisition Is Initialization)是 C++ 中一种资源管理机制,核心思想是:将资源的生命周期与对象的生命周期绑定—— 在对象构造时获取资源,在对象析构时自动释放资源,无需手动管理。
C++11实现RAII--lock_guard unique_lock
std::lock_guard:基础 RAII 锁管理
- 功能:构造时自动加锁,析构时自动解锁,仅支持独占锁(如
std::mutex、std::recursive_mutex)。 - 特点:简单、高效,不支持手动解锁或延迟加锁,适合 “加锁后直至作用域结束才解锁” 的场景。
std::unique_lock:灵活的 RAII 锁管理
- 功能:支持更灵活的锁操作,如延迟加锁、手动解锁、超时锁等,可管理各种锁类型(
std::mutex、std::timed_mutex、std::shared_mutex的独占模式)。
#include <mutex>
std::mutex mtx;
int shared_data = 0;void safe_func() {
// 构造时自动加锁(mtx.lock())
std::lock_guard<std::mutex> lock(mtx);
// 临界区操作(无需手动解锁)
shared_data++;
// 函数结束时,lock析构,自动解锁(mtx.unlock())
}std::timed_mutex timed_mtx;
void try_lock_func() {
// 延迟加锁(不立即获取锁)
std::unique_lock<std::timed_mutex> lock(timed_mtx, std::defer_lock);
// 手动尝试超时加锁
if (lock.try_lock_for(std::chrono::seconds(3))) {
// 成功获取锁,执行临界区
shared_data++;
// 可手动提前解锁(可选)
lock.unlock();
}
// 析构时,若仍持有锁则自动解锁
}
C++17实现RAII--shared_lock
std::shared_lock 是标准库提供的RAII 风格共享锁管理工具,专门用于配合 std::shared_mutex(读写锁)实现 “共享读、独占写” 的并发模式。代码同共享锁。
当需要同时持有多个互斥锁(如 std::mutex、std::shared_mutex 等)时,若按顺序单独获取锁,可能因线程获取锁的顺序不同导致死锁。std::scoped_lock 的解决思路是:原子性地同时获取所有锁(要么全部获取,要么全部等待),避免因部分获取锁而引发死锁。
#include <iostream>
#include <thread>
#include <mutex>
#include <scoped_lock> // C++17 头文件
std::mutex mtx1, mtx2; // 两个需要同时获取的锁
int shared_data1 = 0,
int shared_data2 = 0;
// 同时操作两个共享资源,需要同时持有 mtx1 和 mtx2
void safe_operation(int thread_id) {
// 原子性获取 mtx1 和 mtx2(顺序不影响,内部自动处理)
std::scoped_lock lock(mtx1, mtx2); // 构造时获取所有锁// 临界区:安全操作两个共享资源
shared_data1++;
shared_data2++;
std::cout << "线程" << thread_id << ":data1=" << shared_data1 << ", data2=" << shared_data2 << std::endl;// 析构时自动释放所有锁(无需手动 unlock)
}int main() {
std::thread t1(safe_operation, 1);
std::thread t2(safe_operation, 2);t1.join();
t2.join();
return 0;
}
线程间通信 生产者 - 消费者模型
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>// 共享资源:数据队列
std::queue<int> data_queue;
// 互斥锁:保护共享队列
std::mutex mtx2;
// 条件变量:用于线程通信
std::condition_variable cv;
// 标志位:控制线程退出
bool is_running = true;// 生产者线程:生产数据并通知消费者
void producer() {
for (int i = 1; i <= 5; ++i) {
{
// 用 unique_lock 保护共享队列(自动加锁)
std::unique_lock<std::mutex> lock(mtx2);
// 生产数据
data_queue.push(i);
std::cout << "生产者:生产数据 " << i << ",队列大小:" << data_queue.size() << std::endl;
} // 手动解锁(或离开作用域自动解锁),避免消费者等待时无法获取锁// 通知消费者:有新数据可用
cv.notify_one();
// 模拟生产耗时
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}// 生产结束,通知消费者退出
{
std::unique_lock<std::mutex> lock(mtx2);
is_running = false;
}
cv.notify_one(); // 唤醒最后一次等待的消费者
}// 消费者线程:等待数据并消费
void consumer() {
while (true) {
// 用 unique_lock 保护共享队列
std::unique_lock<std::mutex> lock(mtx2);// 等待条件:队列非空 或 生产结束
// wait() 会释放锁并阻塞,被通知后重新获取锁并检查条件
cv.wait(lock, [] {
return !data_queue.empty() || !is_running;
});// 检查退出条件
if (!is_running && data_queue.empty()) {
std::cout << "消费者:无数据可消费,退出" << std::endl;
break;
}// 消费数据
int data = data_queue.front();
data_queue.pop();
std::cout << "消费者:消费数据 " << data << ",剩余大小:" << data_queue.size() << std::endl;
}
}int main() {
std::thread prod(producer);
std::thread cons(consumer);prod.join();
cons.join();
return 0;
}
04 线程异步和通信
线程同步指多个线程的执行有明确的先后顺序或依赖关系,一个线程需要等待另一个线程完成某个操作后才能继续执行,目的是保证共享资源的访问安全性或执行逻辑的正确性。
线程异步指多个线程的执行相互独立,无需等待彼此,一个线程的执行不会阻塞另一个线程,通常用于 “后台执行任务,不影响主线程进度” 的场景。
promise和future
std::promise 和 std::future 是一对配合使用的异步编程工具,用于在不同线程之间传递数据或异常。一个 promise 对象可以通过 get_future() 方法生成一个与之绑定的 future 对象。当 promise 存储结果后,future 就能获取该结果;若 promise 存储异常,future 会在获取时抛出该异常。
工作流程
- 主线程创建
std::promise<T>对象,并通过get_future()获取关联的std::future<T>。 - 主线程将
promise传递给异步线程(通常通过函数参数),自己保留future。 - 异步线程执行任务,完成后通过
promise.set_value()存储结果(或set_exception()存储异常)。 - 主线程通过
future.get()获取结果(若未完成则阻塞等待,若有异常则抛出)。
注:future.get():获取结果,若 promise 尚未设置结果,会阻塞当前线程直到结果就绪;若已设置,则立即返回。
#include <iostream>
#include <thread>
#include <future> // 包含 promise 和 future// 异步任务:计算结果并通过 promise 传递
void calculate(std::promise<int> prom) {
int result = 100 + 200; // 模拟耗时计算
std::this_thread::sleep_for(std::chrono::seconds(1)); // 模拟耗时// 存储结果(此时 future 会收到通知)
prom.set_value(result);
}int main() {
// 1. 创建 promise 对象(结果类型为 int)
std::promise<int> my_promise;// 2. 获取与 promise 绑定的 future
std::future<int> my_future = my_promise.get_future();// 3. 启动异步线程,将 promise 转移给线程(注意:promise 不可复制,只能移动)
std::thread t(calculate, std::move(my_promise));
t.detach(); // 分离线程(无需等待线程结束,通过 future 获取结果)// 4. 主线程可执行其他操作
std::cout << "主线程可执行其他操作,并等待结果中..." << std::endl;
//std::this_thread::sleep_for(std::chrono::seconds(2)); // 模拟耗时// 5. 通过 future 获取结果(若未准备好则阻塞等待)
int result = my_future.get();
std::cout << "异步任务结果:" << result << std::endl; // 输出:300return 0;
}
packaged_task和future
std::packaged_task(定义在 <future> 头文件中)是一个模板类,用于包装可调用对象(函数、lambda、bind 结果等),并将其与 std::future 绑定。当包装的任务被执行时,其结果会自动存储到关联的 future 中,供其他线程获取。
#include <iostream>
#include <future> // 包含 std::packaged_task
#include <thread>// 任务函数:计算平方
int square(int x) {
return x * x;
}int main() {
// 1. 创建 packaged_task,包装 square 函数(结果类型为 int)
std::packaged_task<int(int)> task(square);// 2. 获取与任务绑定的 future(用于获取结果)
std::future<int> fut = task.get_future();// 3. 启动线程执行任务(需移动 task,因不可复制)
std::thread t(std::move(task), 5); // 传入参数 5
t.join();// 4. 通过 future 获取结果(若未完成则阻塞)
std::cout << "5 的平方 = " << fut.get() << std::endl; // 输出:25return 0;
}
async
async属于高层接口,内部自动封装了 promise、future 和线程管理,直接返回 future,无需手动设置结果。在日常简单场景,优先用 std::async,它简洁、安全,能满足大多数异步获取结果的需求,避免手动管理线程的复杂性。
#include <iostream>
#include <future> // 包含 std::async 和 std::future
#include <chrono> // 包含时间相关函数
#include <thread> // 包含 std::this_thread// 子线程任务:模拟耗时计算(返回计算结果)
int atask(int input) {
std::cout << "子线程开始执行,输入值:" << input << std::endl;// 模拟耗时操作(2秒)
std::this_thread::sleep_for(std::chrono::seconds(2));// 计算结果(输入值 * 2)
int result = input * 2;
std::cout << "子线程执行完毕,结果:" << result << std::endl;
return result;
}int main() { // 主线程
std::cout << "主线程启动" << std::endl;// 1. 用 std::async 启动子线程,执行 async_task,传入参数 10
// std::launch::async 确保任务在新线程(子线程)中执行
std::future<int> fut = std::async(std::launch::async, atask, 10);// 2. 主线程在等待子线程结果时,可执行其他操作
std::cout << "主线程执行其他任务..." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1)); // 模拟主线程工作
std::cout << "主线程其他任务完成,等待子线程结果..." << std::endl;// 3. 主线程获取子线程结果(若未完成则阻塞等待)
int task_result = fut.get();
std::cout << "主线程获取到子线程结果:" << task_result << std::endl;std::cout << "主线程结束" << std::endl;
return 0;
}
05 线程池
线程池(Thread Pool) 是一种预先创建一组线程,并复用它们执行多个任务的机制。其核心思想是:避免频繁创建和销毁线程的开销,通过 “池化” 线程资源,提高多任务并发处理的效率。
线程池的必要性
减少线程创建 / 销毁的开销:
线程是操作系统级资源,创建线程需要分配栈空间、初始化 TLS(线程局部存储)等,销毁线程需要回收资源,这些操作耗时且消耗系统资源。
控制线程数量,避免资源耗尽:
过多线程会导致:CPU 上下文切换频繁、内存耗尽。线程池通过限制最大线程数,避免资源过度消耗,确保系统稳定。
提高任务响应速度
线程池中的线程是预先创建并处于就绪状态的,当新任务提交时,无需等待线程创建即可立即执行,减少任务启动延迟。
简单线程池代码
头文件
#pragma once
#include<thread>
#include<mutex>
#include<vector>
#include <list>
#include <condition_variable>
class XTask
{
public:
virtual void Run() = 0; //纯虚函数,派生类必须实现
};
class XThreadPool
{
public:
//初始化线程池 @param num 线程数量
void Init(int num);
void Start();
void AddTask(XTask* task);
XTask* GetTask();
private:
void Run() ; //线程运行函数
int thread_num_ = 0;//线程数量
std::mutex mux_; //互斥锁
std::vector<std::thread> threads_;//线程容器
std::list<XTask*> tasks_;//任务容器
std::condition_variable cv_; //条件变量
};
头文件的实现
#include "xthreadpool.h"
#include<iostream>
using namespace std;void XThreadPool::Init(int num)
{
unique_lock<mutex> lock(mux_);// 加锁(RAII方式,自动释放)
this->thread_num_ = num;
cout << "初始化线程池,线程数量:" << this->thread_num_ << endl;
}void XThreadPool::Start()
{
unique_lock<mutex> lock(mux_); // 加锁,保护线程容器和状态检查
if(this->thread_num_ <= 0)
{
cerr << "线程池未初始化,无法启动!" << endl;
return;
}
if(!this->threads_.empty())
{
cerr << "线程池已启动,无法重复启动!" << endl;
return;
}
for(int i = 0; i < this->thread_num_; i++)
{
//thread* th = new thread(&XThreadPool::Run, this); // 创建新线程,绑定成员函数Run(this为当前对象指针)
//this->threads_.push_back(std::move(*th));// 将线程对象移动到容器(thread不可复制,只能移动
this->threads_.emplace_back(&XThreadPool::Run, this);
}
}void XThreadPool::Run()
{
cout << "线程 " << this_thread::get_id() << " 运行中..." << endl;
while (true)
{
XTask* task = this->GetTask();
if (!task)continue;
try
{
task->Run();
}
catch (const exception& e)
{
cerr << "任务执行异常:" << e.what() << endl;
}}
}
void XThreadPool::AddTask(XTask* task)
{
unique_lock<mutex> lock(mux_);
this->tasks_.push_back(task);
lock.unlock(); // 提前释放锁,减少锁持有时间
cv_.notify_one(); // 通知一个等待的线程有新任务到来
}XTask* XThreadPool::GetTask()
{
unique_lock<mutex> lock(mux_);
if(this->tasks_.empty())
{
cv_.wait(lock); // 等待任务到来
}
if(tasks_.empty())
{
return nullptr;
}
XTask* task = this->tasks_.front();
this->tasks_.pop_front();
return task;
}
main.cpp
#include "xthreadpool.h"
#include <iostream>
using namespace std;
class MyTask : public XTask
{
public:
void Run() override
{
cout << "任务执行中,线程ID:" << this_thread::get_id() << endl;
cout << "任务名称:" << name << endl;
}
public:
string name;
};int main(int argc, char* argv[])
{
XThreadPool pool;
pool.Init(3);
pool.Start();
MyTask task1;
MyTask* task2 = new MyTask();
task1.name = "Task_1";
task2->name = "Task_2";
pool.AddTask(&task1);
pool.AddTask(task2);
getchar();// 阻塞主线程(等待用户输入,避免程序退出)delete task2;
return 0;
}
更多推荐



所有评论(0)