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::mutexstd::recursive_mutex)。
  • 特点:简单、高效,不支持手动解锁或延迟加锁,适合 “加锁后直至作用域结束才解锁” 的场景。

std::unique_lock:灵活的 RAII 锁管理

  • 功能:支持更灵活的锁操作,如延迟加锁手动解锁超时锁等,可管理各种锁类型(std::mutexstd::timed_mutexstd::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::mutexstd::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 会在获取时抛出该异常。

    工作流程

    1. 主线程创建 std::promise<T> 对象,并通过 get_future() 获取关联的 std::future<T>
    2. 主线程将 promise 传递给异步线程(通常通过函数参数),自己保留 future
    3. 异步线程执行任务,完成后通过 promise.set_value() 存储结果(或 set_exception() 存储异常)。
    4. 主线程通过 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;  // 输出:300

        return 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;  // 输出:25

        return 0;
    }

    async

    async属于高层接口,内部自动封装了 promisefuture 和线程管理,直接返回 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;
    }

    Logo

    Agent 垂直技术社区,欢迎活跃、内容共建。

    更多推荐