(源码+注释版)从线程 到 线程池 C++

1 篇文章 0 订阅
1 篇文章 0 订阅

什么是线程?

        线程(英语:thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。在Unix System V及SunOS中也被称为轻量进程(lightweight processes),但轻量进程更多指内核线程(kernel thread),而把用户线程(user thread)称为线程。

上面是百度百科的解释~

在C++11里的线程

首先得包含#include <thread>头文件,之后才可以使用线程和相关接口函数。

网上也已经有很多讲述线程原理教学的博客,这里不再深入探讨,直接看常见的用法。

        thread t1(funcA);                     // 创建一个线程,func是该线程执行的函数(无参函数)

        thread t2(funcB, "Hello");        // 创建一个线程,并传递参数(线程函数传递的参数应该在后面)

        t1.join();                                  // 主线程会等待子线程t1执行完,并且join是阻塞的

        t1.detach();                             // 子线程与主线程分离,主线程不会等待子线程执行完

        auto id = t1.get_id();               // 获取线程id

        bool idx = t1.joinable();           // 判断线程是否可被join和detach

线程池的实现

  代码+注释 和 你看懂所需要的前置小知识

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <vector>
#include <functional>

using namespace std;

/*
    线程池中用到的前置知识小总结:
    1.emplace_back: 在vector的末尾添加一个元素 和 push_back类似,但是emplace_back可以接受任意参数,
      而push_back只能接受一个参数。两者的底层实现的机制不同。push_back() 向容器尾部添加元素时,首先
      会创建这个元素,然后再将这个元素拷贝或者移动到容器中(如果是拷贝的话,事后会自行销毁先前创建的这个元素)
      而 emplace_back() 在实现时,则是直接在容器尾部创建这个元素,省去了拷贝或移动元素的过程。

    2.lambda表达式
    [capture-list] (parameters) mutable -> return-type { statement }
    [&](int a,int b) {return a+b;}  //lambda表达式,捕获列表为空,参数列表为两个int类型,返回类型为int,函数体为return a+b;

    3.condition_variable 用于线程同步,当某个条件满足时,通知等待的线程继续执行
      使用步骤:
        1.创建一个condition_variable对象。
        2.创建一个互斥锁mutex对象,用来保护共享资源访问。
        3.在需要等待条件变量的地方
         使用unique_lock<mutex> 对象锁定互斥锁
         并调用condition_variable::wait()函数等待条件变量满足。
        4.在其他线程中需要通知等待的线程时,调用condition_variable::notify_one或notify_all通知等待的线程

    4.function和bind

    5.lock_guard 用于保护共享数据,防止多个线程同时访问同一资源而导致的数据竞争问题
      当构造函数被调用时,会自动锁定
      当析构函数被调用时,会自动解锁
      只能在局部作用域使用

    6.unique_lock 用于保护共享数据,防止多个线程同时访问同一资源而导致的数据竞争问题
      unique_lcok 是 lock_guard 的升级版,功能更加强大,使用更加灵活,但开销更大


    7.call_once与其使用场景 只能在线程函数中使用
      单例模式是一种常见的设计模式,由于单例模式在多线程环境中要考虑线程安全问题。

    8.condition_variable 用于线程同步,当某个条件满足时,通知等待的线程继续执行
        使用步骤:
        1.创建一个condition_variable对象。
        2.创建一个互斥锁mutex对象,用来保护共享资源访问。
        3.在需要等待条件变量的地方
          使用unique_lock<mutex> 对象锁定互斥锁
          并调用condition_variable::wait()函数等待条件变量满足。
        4.在其他线程中需要通知等待的线程时,调用condition_variable::notify_one或notify_all通知等待的线程

    9.左值引用move() 和 完美转发 forward()

    10.可变参数和模板函数与万能引用 &&

    11.时间库chrono

    12.作用域
*/

class ThreadPool
{
public:
    ThreadPool(int num_threads) : stop(false)
    {
        for (int i = 0; i < num_threads; ++i)
        {
            // 注意,这里向容器追加的是一个线程函数,而不是线程对象,则可以用lambda表达式来定义
            threads.emplace_back([this]
                {
                    while (1)
                    {
                        unique_lock<mutex> lock(mtx); // 造锁
                        condition.wait(lock, [this] {
                            return stop || !tasks.empty();  // 条件变量,当stop为true或者任务队列不为空时,线程继续执行
                            });

                        if (stop && tasks.empty()) return; // 如果线程终止了,则直接返回

                        function<void()> task(move(tasks.front())); // 取出任务队列中的任务, move 把左值改为右值
                        tasks.pop(); // 弹出任务
                        lock.unlock(); // 解锁
                        task(); // 执行任务
                    }
                });
        }
    }

~ThreadPool()
{
    {
        unique_lock<mutex> lock(mtx);
        stop = true; // 停止线程
    }

    condition.notify_all(); // 通知所有线程
    for (auto& thread : threads) thread.join(); // 等待所有线程结束
}

template<class F, class... Args>
void enqueue(F&& f, Args&&... args)
{
    function<void()>task = bind(forward<F>(f), forward<Args>(args)...); // 完美转发,将参数绑定到任务函数上
    {
        unique_lock<mutex> lock(mtx); //  加锁
        tasks.emplace(move(task)); // 将任务函数添加到任务队列中
    }
    condition.notify_one(); // 通知一个线程
}

private:
    vector <thread> threads;
    queue <function<void()>> tasks;
    mutex mtx;
    condition_variable condition;
    bool stop;
};

int main()
{
    ThreadPool pool(4);
    for (int i = 0; i < 10; ++i)
    {
        pool.enqueue([i] {
            cout << "task: " << i << " is runing " << endl;
            this_thread::sleep_for(chrono::seconds(1));
            cout << "task: " << i << " is done " << endl;
            });
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值