如何在网站上做背景图片怎么做,哪里可以在百度做网站,公司比较好,网站建设建设报价【导读】#xff1a;C 线程池一直都是各位程序员们造轮子的首选项目之一。今天#xff0c;小编带大家一起来看看这个轻量的线程池#xff0c;本线程池是header-only的#xff0c;并且整个文件只有100行#xff0c;其中C 的高级用法有很多#xff0c;很值得我们学习#… 【导读】C 线程池一直都是各位程序员们造轮子的首选项目之一。今天小编带大家一起来看看这个轻量的线程池本线程池是header-only的并且整个文件只有100行其中C 的高级用法有很多很值得我们学习一起来看看吧。以下是正文线程池C 带有线程操作异步操作就是没有线程池至于线程池的概念我先搜一下别人的解释一般而言,线程池有以下几个部分:1. 完成主要任务的一个或多个线程。2. 用于调度管理的管理线程。3. 要求执行的任务队列。我来讲讲人话你的函数需要在多线程中运行但是你又不能每来一个函数就开启一个线程所以你就需要固定的N个线程来跑执行但是有的线程还没有执行完有的又在空闲如何分配任务呢你就需要封装一个线程池来完成这些操作有了线程池这层封装你就只需要告诉它开启几个线程然后直接塞任务就行了然后通过一定的机制获取执行结果。这里有一个100行实现线程池的操作https://github.com/progschj/ThreadPool/blob/master/ThreadPool.h分析源代码 头文件#include #include #include #include #include #include #include #include #include vector,queue,momory 都没啥说的thread线程相关mutex 互斥量解决资源抢占问题condition_variable 条件量用于唤醒线程和阻塞线程,future 从使用的角度出发它是一个获取线程数据的函数。functional 函数子可以理解为规范化的函数指针。stdexcept 就跟它的名字一样标准异常。class ThreadPool {public: ThreadPool(size_t); templateclass F, class... Args auto enqueue(F f, Args... args) - std::futuretypename std::result_of::type; ~ThreadPool();private: // need to keep track of threads so we can join them std::vector std::thread workers; // the task queue std::queue std::function tasks; // synchronization std::mutex queue_mutex; std::condition_variable condition; bool stop;};线程池的声明构造函数一个enqueue模板函数 返回std::future, 然后这个type又利用了运行时检测还是编译时检测?推断出来的非常的amazing啊。成功的使用一行代码反复套娃这高阶的用法就是大佬的水平吗i了i了。workers 是vector:thread 俗称工作线程。std::queuestd::function tasks 俗称任务队列。那么问题来了这个任务队列的任务只能是void() 类型的吗感觉没那么简单还得接着看呐。mutex,condition_variable 没啥讲的,stop 控制线程池停止的。// the constructor just launches some amount of workersinline ThreadPool::ThreadPool(size_t threads) : stop(false){ for(size_t i 0;i workers.emplace_back( [this] { for(;;) { std::function task;{ std::unique_lock:mutex lock(this-queue_mutex); this-condition.wait(lock, [this]{ return this-stop || !this-tasks.empty(); }); if(this-stop this-tasks.empty()) return; task std::move(this-tasks.front()); this-tasks.pop(); }task(); } } );}大佬写的注释就是这么朴实无华说这个构造函数仅仅是把一定数量的线程塞进去我是看了又看才悟出来这玩意是什么意思……虽然本质上的确是它说的只是把线程塞进去但是这个线程也太绕了。workers.emplace_back 参数是一个lambda表达式不会阻塞也就是说最外层的是一个异步函数每个线程里面的事情才是重点。labmda表达式中最外层是一个死循环至于为什么是for(;;)而不是while(1) 这虽然不是重点不过大佬的用法还是值得揣摩的我估计是效率会更高task 申明后紧跟着一个大括号这个{}里面的部分是一个同步操作至于为什么用this-lock 而不是直接使用[]来捕获参数想来也是处于内存考虑。精打细算的风格像极了抠门的地主i了i了。紧接着一个wait(lock,condtion)的操作像极了千层饼的套路。第一层这TM不是要锁死自己啊这样不是构造都得卡死第二层我们看到它emplace_back了一个线程不会阻塞但是等开锁锁不就在它自己的线程里面嘛那不得锁死了啊第三层我们看到这个lock其实只是个包装真正的锁是外层的mutex所以从这里是不存在死锁的。但是你的wait的condition怎么可能不懂呢必须要 stop 或者 !empty 才wait吗第四层我们查资料发现后面的condition是返回false才会wait也就是说要!stop empty才会wait就是说这个线程池是 运行态并且没有任务才才会执行等待操作否则就不等了直接冲第五层既然你判断了上面判断了stop和非空为啥下面还要判断stop和空才退出呢不显得冗余第六层要确定它的确是被置为stop了且队列执行空了它才能够光荣退休。有没有问题呢有最后所有线程都阻塞了你stop置为true它们也不知道啊……我估计它的stop会有唤醒所有线程的操作不过如果有的在执行有的在等待应该没办法都通知到位但是在执行的在下一次判断的时候也能正常退出。因为有了疑惑我们就想看stop相关的操作结果发现放在了析构函数里面……// the destructor joins all threadsinline ThreadPool::~ThreadPool(){ { std::unique_lock:mutex lock(queue_mutex); stop true; } condition.notify_all(); for(std::thread worker: workers) worker.join();}{}里面上锁进行了stop为true的操作至于为什么不用原子操作我也不知道但是仔细想了下大概是因为本来就有一把锁了再用原子就不是内味儿了。然后它果然通知了所有并且还把工作线程join了。也就是等它们结束。结束了千层饼の解析之后我们看看最重要的入队操作// add new work item to the pooltemplateclass F, class... Argsauto ThreadPool::enqueue(F f, Args... args) - std::futuretypename std::result_of::type{ using return_type typename std::result_of::type;auto task std::make_shared std::packaged_task ( std::bind(std::forward(f), std::forward(args)...) ); std::future res task-get_future(); { std::unique_lockstd::mutex lock(queue_mutex);// dont allow enqueueing after stopping the pool if(stop) throw std::runtime_error(enqueue on stopped ThreadPool);tasks.emplace([task](){ (*task)(); });