c++11笔记 跨平台线程池

1. 左值:

简单的说,可以放在等号左边的变量可以称之为左值,可以对该变量进行取地址运算的是左值,左值在内存中有确切的地址,可以长期存在,拥有具体的名字的。

比如

int a = 10;
int *p = &a;//这里的a就是左值


2. 右值:

简单的说可以放到等号右边的值叫做右值,右值不能进行取地址运算,右值在内存中没有确切的地址,不能再内存中长期存在,没有具体的名字,是一个临时对象。

比如:

cout << 10 << endl;
cout << &10 <<endl;//error

3. 变长参数模板:
模板参数包:
 

template<typename F, typename... Args>
void Show(F&& f, Args&&... args)//函数名,以及函数的参数
{}

4. 万能引用,引用折叠和完美转发

万能引用,引用折叠,完美转发的关系-CSDN博客

5. 方法bind

bind(fuction, parameter,...);//将参数绑定在函数中,生成一个新的可调用对象

可以使用placeholders_1、placeholders_2...作为占位符,表示新的可调用对象调用时,由外部提供值。(placeholders就是占位符的意思)

6. 模板类fuction

模板类fuction是一个函数封装器,<>模板参数限制了fuction模板类对象的使用方法(返回值是什么,参数列表是什么),但这不意味着对封装在fuction中的函数参数和返回值进行限制。我们可以像使用函数一样使用fuction模板类对象。相当于对函数再进行封装了一遍,使得我们可以以调用function对象的方式来调用该对象中封装进去的函数。

比如:

std::fuction<void()>(std::bind(forward<F>(f), forward<Args>(args));
//利用完美转发保存参数的原始类型,将参数bind到函数上,生成一个新的函数对象
//再将函数对象封装进fuction函数中
//可以以不带参数和返回值的方式调用如:fuction();

condition_variable

condition_variable可以用来定义条件变量,条件变量可以多线程中唤醒和堵塞线程,可以使用condition.wait(lock, fuction)

第一个参数是锁lock,我们在使用wait的使用将线程堵塞,不需要锁,因此将锁传进去就会解开锁(我们不希望线程在持有互斥锁的时候进入休眠)。第二个参数是可调用对象(函数,函数指针,函数对象,lambda表达式),该可调用对象的返回值必须是Bool类型,当返回值为true且被notify通知的时候,将会上锁,等待取消,线程继续执行,否则线程将一直堵塞。(通知和堵塞机制是为了,解决线程被在获得cpu资源的时候,不断循环等待(看看资源准备好了吗,一直访问资源区),等待生产者将任务加入到任务队列中,我们都知道这样的繁忙的等待是非常低效的,通知和堵塞机制就是为了解决这种问题)

为什么线程执行到wait的时候解锁后进入休眠,这是因为这样就可以做到只有生产者将生产资料放入到任务队列钟后,被通知的线程可以继续执行,其他没有被通知的线程继续等待,即按照拟定的序列进行执行。

也就就是说condition_variable会有一个上锁和解锁的操作,当执行sleep(堵塞)的时候,锁将会被释放,当被唤醒的时候,锁将会被锁上,然后判断可调用对象,来判断是否可以继续执行,可调用对象返回false则继续堵塞,返回true则执行。

第二个参数是为了解决虚假唤醒(spurious wake)的,这样只有继续执行的条件被满足的时候,线程才可以继续运行。也就是在唤醒后再进行一次判断,防止线程对没有准备好的资源进行操作。

C++并发编程之 6_Condition Variable_哔哩哔哩_bilibili

7. emplace_back和push_back的区别:

emplace_back和push_back在大部分情况下是等价的,只有在直接调用构造函数创建对象时,emplace_back更高效。然而,使用emplace_back时需要注意可能出现的危险性,因为它的行为可能不符合预期。因此,推荐默认使用push_back,只有在需要临时创建对象时才使用emplace_back。

push_back和emplace_back的区别在于它们调用的构造函数不同,线程不支持push_back因为thread将复制构造函数和移动构造函数以及赋值运算符都给delete(禁止)了,因此我们应该使用emplace_back。

8. 时间库chrono

用来时间管理的,在标准库中

如:

this_thread:sleep_for(std::chrono::second(1));
//让线程休息一秒钟

9. unique_lock

一种互斥量封装类,创建该封装类对象的时候,会对互斥量进行加锁,当对象结束使用后,自动调用析构函数进行解锁。相对去auto_lock,该unique_lock更加灵活,提供更多实用的方法,比方说,try_lock尝试加锁,得就true不得就false,try_lock_for,一直加锁,直到加锁成功或者超时,以及try_lock_until等等

10. 什么是虚假唤醒(spurious wakeup)?

虚假唤醒就是你(作为一个线程)收到了其他线程传来的唤醒信号,但是你唤醒后发现别的线程处理的比你快,此时没有数据被可以用于操作,这种情况的发生是预期之外的,被称为虚假唤醒。此外,被唤醒的线程中,只有一部分线程是真正满足唤醒条件的,剩下的部分即使被唤醒,也无法进行有效的操作,这些被唤醒但无法操作的线程,也被视为发生了虚假唤醒。

解决虚假唤醒的常用手段是在检查条件时使用循环(通常是while循环),而不是只进行一次检查。这样,即使发生了虚假唤醒,线程也会再次检查条件,如果条件不满足,线程会再次进入等待状态。

c++11推出了一种更加简单的方式来解决虚假唤醒(唤醒后没有数据可以供该线程来使用),那就是condition_variable中的wait,被唤醒后会先判断wait中可调用对象的返回值是否为true,只有为true的时候,该线程才会继续执行,否则该线程将会被继续堵塞。

#include <condition_variable>condition_variable condition;std::unique_lock<std::mutex> lock(mutex)
condition.wait(lock, [this]{return !tasks.empty();});

有了以上的前置知识

手写一个线程池:

#include <iostream>
#include <vector>
#include <thread>
#include <queue>
#include <mutex>
#include <functional>
#include <condition_variable>class ThreadPool
{public://线程池的构造函数,用来创建线程池ThreadPool(int ThreadNum) : stop(false){for(int i = 0; i < ThreadNum; i++){//创建线程: 线程的入口函数,因为thread已经将复制构造函数和移动构造函数给delete了,只能使用emplacethreads.emplace_back([this] {while (true){std::unique_lock<std::mutex> lock(mutex);condition.wait(lock, [this] {return stop || !tasks.empty(); });//只有lambda表达式返回true的时候继续执行,否则sleep,解决虚假唤醒问题if (stop && tasks.empty())//当线程池停止且任务队列为空的时候,线程结束;{return;}std::function<void()> task(std::move(tasks.front()));//使用移动构造函数来创建tasktasks.pop();lock.unlock();task();//任务执行的时候不需要锁,应该将锁进行释放}					});}}//边长参数模板,万能引用,引用折叠,完美转发template<typename F, typename... Args>void enqueue(F&& f, Args&&...args){std::function<void()> task(std::bind(std::forward<F>(f), std::forward<Args>(args)...));//forward完美转发,保留原始类型,作为参数,bind根据参数生成新对象,该新对象作为function的参数{std::unique_lock<std::mutex> lock(mutex);tasks.emplace(std::move(task));//移动构造函数生成新对象}condition.notify_one();//通知一个唤醒;}~ThreadPool(){{std::unique_lock<std::mutex> lock(mutex);//因为stop是共享变量,使用之前应该先上锁stop = true;}//代码块运行结束,lock锁自动调用析构函数进行解锁condition.notify_all();//唤醒所有线程for (auto& thread : threads) //让每个线程运行到停止{thread.join();}}private:std::vector<std::thread> threads;//线程数组;std::queue<std::function<void()>> tasks;//任务队列;std::condition_variable condition;std::mutex mutex;bool stop;};int main(void)
{ThreadPool pool(4);for (int i = 0; i < 10; i++){pool.enqueue([i] {std::cout << "Task: " << i << " is running." << "Thread Id: " << std::this_thread::get_id() << std::endl;std::this_thread::sleep_for(std::chrono::seconds(1));std::cout << "Task: " << i << "is done" << std::endl;});}return 0;
}


 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/761121.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java 继承(下)

前面我们已经说明了什么是继承&#xff1f;继承的好处弊端等&#xff0c;不清楚的可参照链接 java 继承&#xff08;上&#xff09;-CSDN博客 本篇文章主要理解 继承中变量&#xff0c;构造方法&#xff0c;成员方法的访问特点。 1、继承中变量的访问特点 1.1 代码实现 不看…

亚马逊认证考试系列 - 知识点 - 安全组简介

AWS安全组是一种虚拟防火墙&#xff0c;用于控制实例进出网络流量。安全组是一个实例级别的防火墙&#xff0c;可以定义哪些流量可以进入或离开特定的EC2实例。 功能&#xff1a;安全组可以用于限制特定类型的流量&#xff0c;如HTTP或SSH&#xff0c;允许特定IP地址范围的流量…

若依添加页面

背景&#xff1a;我想增加的是一个收支管理的页面 views中直接添加income文件夹&#xff0c;里面放着index.vue 网页的菜单中添加这个页面的菜单

基于springboot的留守儿童爱心网站

技术&#xff1a;springbootmysqlvue 一、系统背景 现代社会&#xff0c;由于经济不断发展&#xff0c;旧物捐赠的数量也在不断的增加&#xff0c;人们对留守儿童爱心信息的需求也越来越高。 以往的留守儿童爱心的管理&#xff0c;一般都是纸质文件来管理留守儿童爱心信息&am…

分布式异步任务框架celery

Celery介绍 github地址&#xff1a;GitHub - celery/celery: Distributed Task Queue (development branch) 文档地址&#xff1a;Celery - Distributed Task Queue — Celery 5.3.6 documentation 1.1 Celery是什么 celery时一个灵活且可靠的处理大量消息的分布式系统&…

rust学习(简单handler实现)

用过android的同学对于handler应该都很了解&#xff0c;用起来比较方便。这里用rust设计了一个简单的rust。 1.处理接口 pub trait ProcessMessage {fn handleMessage(&self,msg:Message); } 2.Message结构体 pub struct Message {pub what:u32,pub arg1:i32,pub arg2:…

Java:设计模式

文章目录 参考简介工厂模式简单工厂模式工厂方法模式抽象工厂模式总结 单例模式预加载懒加载线程安全问题 策略模式 参考 知乎 简介 总体来说设计模式分为三类共23种。 创建型模式&#xff0c;共五种&#xff1a;工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模…

Function与Module的差异与应用场景,symbolic() 和 forward() 是什么关系?

Function与Module的差异与应用场景 Function与Module都可以对pytorch进行自定义拓展&#xff0c;使其满足网络的需求&#xff0c;但这两者还是有十分重要的不同&#xff1a; Function一般只定义一个操作&#xff0c;因为其无法保存参数&#xff0c;因此适用于激活函数、pooli…

Unity构建详解(1)——SBP介绍

【前言】 Unity的资源工作流程分为导入、创建、构建、分发、加载。我们说的是其中的构建步骤。 构建是指将项目工程中的资源文件和代码整合程可执行文件的过程&#xff0c;构建的结果是生成可执行文件&#xff0c;在win平台上是exe&#xff0c;在Android平台上是apk&#xff…

基于浏览器localStorage作为数据库完成todolsit项目

一、文章内容 TodoList结构搭建HTML代码 TodoList样式编写Css代码 TodoList行为表现JavaScript代码 二、项目展示 项目介绍 Todolist是一个基于B/S模式开发的待办事项软件&#xff0c;主要功能是离线记录用户的待办事项和已经完成的事情&#xff0c;基于htmlcssjs实现&am…

Superset二次开发之PostgreSQL 存储库介绍

Apache Superset 使用 PostgreSQL 作为其默认的元数据数据库,来存储关于数据源、图表、仪表盘、用户及其权限等信息。下面是列出的一些主要表的功能和作用的简介: 权限和角色 ab_permission: 存储权限,如“可以访问仪表板”、“可以执行SQL查询”等。ab_permission_view: 将…

Mysql——索引下推

MySQL的索引下推&#xff08;Index Condition Pushdown, ICP&#xff09;是一种查询优化技术&#xff0c;它允许MySQL在存储引擎层执行部分WHERE子句中的过滤条件&#xff0c;而非全部在MySQL服务器层执行。这使得在扫描索引过程中就可以剔除不满足条件的记录&#xff0c;从而减…

【C++】---string的模拟

【C】---string的模拟 一、string类实现1.string类的构造函数2.swap&#xff08;&#xff09;函数3.拷贝构造函数4.赋值运算符重载5.析构6.迭代器7.operator[ ]8.size9.c_str&#xff08;&#xff09;10.reserve&#xff08;&#xff09;11.resize&#xff08;&#xff09;12.p…

flutter 局部view更新,dialog更新进度,dialog更新

局部更新有好几种方法&#xff0c;本次使用的是 StatefulBuilder 定义 customState去更新对话框内容 import package:flutter/cupertino.dart; import package:flutter/material.dart;class ProgressDialog {final BuildContext context;BuildContext? dialogContext;double _…

【DL经典回顾】激活函数大汇总(四十一)(SinReLU附代码和详细公式)

激活函数大汇总(四十一)(SinReLU附代码和详细公式) 更多激活函数见激活函数大汇总列表 一、引言 欢迎来到我们深入探索神经网络核心组成部分——激活函数的系列博客。在人工智能的世界里,激活函数扮演着不可或缺的角色,它们决定着神经元的输出,并且影响着网络的学习能…

Oracle函数6—递归查询(start with...connect by、sys_connect_by_path、level)

文章目录 一、准备数据二、基本使用三、level函数四、获取完整的全树路径 一、准备数据 创建表 CREATE TABLE TEST_ORG (ID VARCHAR2(64) NOT NULL PRIMARY KEY,NAME VARCHAR2(200),PARTEN_ID VARCHAR2(64) ); comment on column TEST_ORG.ID is 主键; comment on column TES…

C语言经典例题(2) --- 阶乘、斐波那契数、9*9乘法表、字符串逆序、求和

文章目录 1.求n的阶乘。(不考虑溢出)2.求第n个斐波那契数。&#xff08;不考虑溢出&#xff09;3.屏幕上输出9*9乘法口诀表4.字符串逆序(递归实现)5.计算一个数的每位之和(递归实现) 1.求n的阶乘。(不考虑溢出) #include <stdio.h>int fac(int n);int main() {int n 0;…

8节点空间壳单元Matlab有限元编程 | 曲壳单元 | 模态分析 | 3D壳单元 | 板壳理论| 【源代码+理论文本】

专栏导读 作者简介&#xff1a;工学博士&#xff0c;高级工程师&#xff0c;专注于工业软件算法研究本文已收录于专栏&#xff1a;《有限元编程从入门到精通》本专栏旨在提供 1.以案例的形式讲解各类有限元问题的程序实现&#xff0c;并提供所有案例完整源码&#xff1b;2.单元…

Mysql的行级锁

MySQL 中锁定粒度最小的一种锁&#xff0c;是 针对索引字段加的锁 &#xff0c;只针对当前操作的行记录进行加锁。 行级锁能大大减少数据库操作的冲突。其加锁粒度最小&#xff0c;并发度高&#xff0c;但加锁的开销也最大&#xff0c;加锁慢&#xff0c;会出现死锁。行级锁和存…

数据结构面试常见问题之Insert or Merge

&#x1f600;前言 本文将讨论如何区分插入排序和归并排序两种排序算法。我们将通过判断序列的有序性来确定使用哪种算法进行排序。具体而言&#xff0c;我们将介绍判断插入排序和归并排序的方法&#xff0c;并讨论最小和最大的能区分两种算法的序列长度。 &#x1f3e0;个人主…