首页 > 编程语言 >c++一些零碎记录

c++一些零碎记录

时间:2023-06-16 20:01:44浏览次数:32  
标签:std 函数 记录 int lock 零碎 线程 c++ mutex

c++11

alignas
struct alignas(8) S {} //定义结构体 同时指定分配给结构体的大小为8字节
alignof(与内存对齐相关)
struct obj{char a;int b;}
alignof(obj)=4;   //alignof(obj)=4表示对于obj,其内存对齐是以多少字节为单位对齐
对于单个变量char 其alignof(char)=1,单个字节对齐

and_eq => &=
bitand 按位& bitor 按位|

a bitand b; a bitor b;

decltype

decltype(double) y; y为double类型

decltype((double)) z=y;//decltype为double&类型, double& z=y(double);

template<typename T,typename U>
auto add(T a,U b) -> decltype(a+b)//根据传入的参数类型来确定最终返回的类型
{
  return a+b;
  return (c);//auto c=a+b,返回的是引用
}
explicit明确类型
struct A{
 operator int() const {return 0;}//A实例化的对象可以转换为int类型int s=a(弱转)
}
nullPointer(nullptr)

int* a=nullptr;

类的前置声明

在一个类中要使用下面的类,需先在要使用的类前声明被使用的类

同时在该类中,只能使用声明的类的指针传参(因为编译器并不知道声明的类的大小,不知道传值传多少,指针大小与系统有关,大小已知)

同时在头文件中若是并没有使用到另一个头文件的类,只是使用另一头文件的类的指针,则可以只在头文件中声明使用类指针的类,没有必要include 相关头文件

如果在类中使用声明的类,则要include

在一个头文件里加前置类声明还是include头文件

对于A类:要include头文件,因为编译器要知道继承的A类的具体成员

B同理

C类:在Good里成员变量m_cGroup的大小受C具体大小的影响

D类:D是其固定类成员,要知道大小 D m_dObject

E类:可以去掉include "E.h" ,class E;前缀声明即可;对于编译器来说,只需要知道E是作为返回值类型即可,不需知道具体大小。

需include :作为基类、作为类成员

作为函数参数接口、返回值,只需声明即可

类的一些拷贝函数

在一个类中,不希望有拷贝构造函数,同时不希望编译器自动生成相关函数,可以在类内声明拷贝构造函数,但是不去实现它。

在c++11中,对于废弃的函数;不需要的函数,同时不要编译器自动生成,在函数声明上可以:

RuleOfThree(const RuleOfThree& other)=delete;
RuleOfThree& operator=(const RuleOfThree& other)=delete;
左值与右值

对于不能取地址的是右值 如:auto add=&(2+3); error

右值引用:int& e=a;//左值引用 int& le=2;//error int&& re=2;//右值引用

值得一提的是,左值的英文简写为“lvalue”,右值的英文简写为“rvalue”。很多人认为它们分别是"left value"、"right value" 的缩写,其实不然。lvalue 是“loactor value”的缩写,可意为存储在内存中、有明确存储地址(可寻址)的数据,而 rvalue 译为 "read value",指的是那些可以提供数据值的数据(不一定可以寻址,例如存储于寄存器中的数据)。
  1. 左值可以当作右值
  2. 有名称的、可以获取到存储地址的表达式即为左值;反之则是右值。
  3. &左值引用,只能引用左值,无法对右值添加引用 ;常量引用可以引用右值 const int& c=10;
  4. 和声明左值引用一样,右值引用也必须立即进行初始化操作,且只能使用右值进行初始化
  5. 右值引用可以对右值进行修改 int&& a=10; a=100;
  6. 将左值std::move()为右值,其地址是一样的
int main()
{
  std::vector<int> a;//左值
  for(int i=0;i<10;i++) a.push_back(i);
  auto b=a;//b也是左值
  auto& c=b;//c左值引用
  auto&& d=std::move(b);//std::move(b)将左值b转成右值,返回右值vector<int>&&
  //
}

std::move(b)返回右值,调用拷贝构造函数 const RuleOfFive&常量引用可以接收右值

将b的右值(里的数据)转为左值,之后不在使用b

右值拷贝移动到左值后,不再使用右值,不确定右值

对于e=std::move(e);相同的对象需考虑

print()函数里if(m_value)注释掉 改为assert(m_value);

一些类内变量的初始化(对于常量成员变量和引用成员常量 必须用初始化成员列表方式)

初始化列表中成员变量的初始化与列表的顺序无关,与类内成员变量的声明顺序相关(考虑垃圾值)如:

int x;

int y;

X(int m):y(m),x(y)//先初始化x,y还未知道具体,y是垃圾值,x是垃圾值

{}

#include <iostream>
using namespace std;
 
class BClass
{
public://此处i为一般的常量,ci为常量变量,ri为引用成员变量,仅i可以再构造函数内初始化
    BClass() : i(1), ci(2), ri(i)            // 对于常量型成员变量和引用型成员变量,必须通过
    {                                             // 参数化列表的方式进行初始化。在构造函数体内进
    }                                             // 行赋值的方式,是行不通的。
    
private:
    int i;                                                          // 普通成员变量
    const int ci;                                             // 常量成员变量
    int &ri;                                                      // 引用成员变量
    static int si;                                             // 静态成员变量
    //static int si2 = 100;                             // error: 只有静态常量成员变量,才可以这样初始化
    static const int csi;                                // 静态常量成员变量
    static const int csi2 = 100;                   // 静态常量成员变量的初始化(Integral type)    (1)
    static const double csd;                      // 静态常量成员变量(non-Integral type)
    //static const double csd2 = 99.9;      // error: 只有静态常量整型数据成员才可以在类中初始化
};
 
// 静态成员变量的初始化(Integral type)
int BClass::si = 0;//不能写成int static BClass::si=0;即不能出现static

// 静态常量成员变量的初始化(Integral type)
const int BClass::csi = 1;//此处也一样,要么在定义时初始化,要么在此处初始化,但不能加static
 
// 静态常量成员变量的初始化(non-Integral type)
const double BClass::csd = 99.9;//同上
 
int main(void)
{
    BClass b_class;
    b_class.print_values();
 
    getchar();
    return 0;
}
类的析构函数(不要在析构函数里抛出异常)

类的析构函数默认不抛出异常:~B() noexcept{}

改为允许抛出异常 ~B() noexcept(false) {}

try{}catch(std::string const& e){}//假设抛出的异常为string类型
class A;
class B :public A
{
public:
   B(){}
   inline ~B() noexcept
   //在析构函数里会调用成员的析构函数和基类的析构函数,在基类里有调用其基类析构函数,可能会很长
   {
    
   }
}
inline内联函数

内联函数一般都是1-5行的小函数, 谨慎对待析构函数, 析构函数往往比其表面看起来要更长, 因为有隐含的成员和基类析构函数被调用;

一般要是想阻止编译器生成析构函数 ~Class(); 声明

类的构造函数

类的构造函数失败应该抛出异常

group() :m_string(new char[10]) {
throw std::string("error");
}
虚函数

在基类的析构函数,要加virtual

基类指针=派生类new 通过delete 基类指针,即销毁真正new出来的派生类内存

将一个类指针转为另一个类指针(把基类指针转为派生类指针)

:group* g=static_cast<group*> base;

:group* g=dynamic_casty<group*> base;

(1)析构函数定义为虚函数时:基类指针可以指向派生类的对象(多态性),如果删除该指针delete []p;就会调用该指针指向的派生类析构函数,而派生类的析构函数又自动调用基类的析构函数,这样整个派生类的对象完全被释放。

(2)析构函数不定义为虚函数时:编译器实施静态绑定,在删除基类指针时,只会调用基类的析构函数而不调用派生类析构函数,这样就会造成派生类对象析构不完全。

在一个基类的构造函数和析构函数中调用其虚函数,实际调用其本身的虚函数,不会调用派生类重写的函数

虚函数特性不存在

一般一个类内没有virtual虚函数,一般不用把析构函数设为虚析构函数

new

new除了分配内存外,还调用相关构造函数对内存数据初始化

智能指针
std::shared_ptr<int> p(new int(10));
共享指针p指向new出来的10 整型

其基类是ObjectPtr p 函数参数(const ObjectPtr& obj)

各个类里面都有指针指向另一个类

weak_ptr 检查资源是否存在

weak_ptr不使用监控的资源,要想使用资源,需转为shared_ptr 通过weak_ptr.lock()得到shared_ptr访问资源

auto resource =wp.lock();如果指向的资源已被释放,返回nullptr

use_count()返回资源的管理者,不包含监听的weak_ptr

通过shared_ptr直接初始化,也可以通过隐式转换来构造;
允许移动构造,也允许拷贝构造。
weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。
w.user_count():返回weak_ptr的强引用计数;
w.reset(…):重置weak_ptr。释放被管理对象的所有权。
shared_fom_this()

在一个类中,没有定义初始化类本身的智能指针,但在类的函数里又需要传入本类的智能指针

把类本身作为智能指针传参

在类定义时:

class child : public std::enable_shared_from_this<child>
{}

在需要传参的函数参数里void function(shared_from_this(),...);
unique_ptr

std::move(unique_ptr)转为右值 原unique_ptr不在管理资源,

被移动资源的“老”智能指针内的成员指针已经被置空,再次使用的话会访问到一个nullptr

将unique_ptr转为shared_ptr : shared_ptr ptr(std::move(unique_ptr)); 原unique_ptr不在管理资源

lambda

inline函数可作为对象变量,形参

[]存储lambda表达式要捕获的值,()内的参数为形参,可供外部调用传值。lambda表达式可以直接调用

值的方式来捕获的。所以无法在lambda表达式内部修改age和name的值

以引用方式捕获的。可以在lambda表达式中修改局部变量的值。
C++的lambda表达式虽然可以捕获局部变量的引用,达到类似闭包的效果,但不是真的闭包,局部变量仍旧有其局部生命周期


原函数的几个参数通过bind绑定传值,返回一个新的可调用对象

//绑定全局函数
auto newfun1 = bind(globalFun2, placeholders::_1, placeholders::_2, 98, "worker");
//相当于调用globalFun2("Lily",22, 98,"worker");
newfun1("Lily", 22);

可以用function存储形参和返回值相同的一类函数指针,可调用对象,lambda表达式等

   auto fname = [](string name)
   {
       cout << "this is auto  " << endl;
       cout << "hello " << name << endl;
   };

   fname("Rolin");
   
typedef void (*P_NameFunc)(string name);
// 3 函数指针
P_NameFunc fname2 = [](string name)
{
    cout << "this is P_NameFunc " << endl;
    cout << "hello " << name << endl;
};

fname2("Vivo");
-----------------------
也可以通过function对象接受lambda表达式
    function<void(string)> funcName;
    funcName = [](string name)
    {
        cout << "this is function " << endl;
        cout << "hello " << name << endl;
    };

    funcName("Uncle Wang");

外面的变量,用到就以引用的方式使用,若是=表示:外部的变量要是用到就用拷贝的方式拷贝使用

也可以auto lacal=[a,b,c] () { }

一些STL
array

对原生数组的封装: std::array<int,10> a={1};{1}初始化为1

内存分配在栈

对于swap函数,array交换每一个元素,array里的元素较多时,效率较低

访问:array.at(i) array[i] array.front(); array.back();

auto info=std::get<1>(array);读array下标为1的元素

    std::array<char,5> arr={'a','b','c','d','e'};
    std::cout<<std::get<1>(arr)<<std::endl;  //==>b
vector的swap只交换指针

vector.insert(vector.end(),10);//在尾部插入一个10

vector.insert(vector.end(),10,5.4f);//在尾部插入10个5.4f emplace(b.end(),10);//类似

deque

可以头尾访问元素(双端队列)

list

list.remove(1.0f);//删除所有符合的值

list.remove_if([](auto v) {return v>100.0f;});//删除v里所有大于100.0f的值

set

set不允许重复,元素自动排序,O (logn)

std::set<(float)> s;

set<person,compareAge> s;//按照仿函数排序

对于set查找,可以使用STL提供的find() :iter=std::find(set.begin(),set.end(),aa);//aa为查找的元素,查找是按照类重载的==完全相同才符合的方式。

对于set本身的find(),:set.find(aa);//find()是根据set提供的仿函数查找的,只要有相关项即符合

multiset

可重复

map

std::map<const key,value> m; 能根据仿函数进行相关操作,不能存重复值

map 返回值是pair<const int,std::string>&

map可以使用insert插入 map.insert(std::make_pair(100,"pair"));

map里存的是pair

map中

auto& findinfo=map.at[10];//查找key值为10的,有就返回value,没有就抛出异常

需要在try{}catch(){}里用

C++ 11 标准中,还为 map 容器增添了移动构造函数。当有临时的 map 对象作为参数,传递给要初始化的 map 容器时,此时就会调用移动构造函数。举个例子:
#创建一个会返回临时 map 对象的函数
std::map<std::string,int> disMap() {
    std::map<std::string, int>tempMap{ {"C语言教程",10},{"STL教程",20} };
    return tempMap;
}
//调用 map 类模板的移动构造函数创建 newMap 容器
std::map<std::string, int>newMap(disMap());
unordered_map

unordered_map使用要对第一项hash

针对Position单独特化

hash:

多线程原子atomic
# include<atomic>
std::atomic<int> m_count;//对要进行原子操作的变量包装
mutex

在临界资源里定义互斥锁

std::mutex m_mutex;//作为变量
m_mutex.lock();//对临界资源加锁
m_mutex.unlock();//对临界资源解锁

STL lock死锁问题:

std::lock(a.mutex,b.mutex,...);//要锁住对象的锁
std::lock_guard<std::mutex> locka(a.mutex,std::adopt_lock);//只负责释放a.mutex锁
vector<> 容器的 emplace_back()向容器尾部插入数据

在操作容器时可以调用对应类型的构造数,在插入对象时会调用相关构造函数,向容器中插入对象数据时,可以传入其构造函数数据,在插入到容器时创建对象(隐式创建)

多线程
t.join();//主线程阻塞,等待子线程结束
t.detach();//主线程不阻塞等待子线程,主线程退出后的子线程由运行时库管理;使用detach不能在使用join
t.joinable();//判断是否能join或detach
std::this_thread::get_id()获取线程id
使用类创建多线程
 #include<iostream>
 #include<unistd.h>
 #include<thread>
 class Thread{
 public:
     void operator()(int num)
     {
         std::cout<<"类线程"<<std::endl;
     }
 };                                                                                                                                                                  
 int main()
 {
     Thread a;//对象
     std::thread t(a,10);//使用对象开启线程,对象拷贝构造函数到子线程
     if(t.joinable()) t.join();
     std::cout<<"主线程"<<std::endl;
  }

使用detach结束,若是子线程使用了主线程的资源,会出现主线程结束,子线程仍在使用主线程资源

传递临时对象作为线程参数
thread t(function,agrc,argc);//函数带参

使用detach():

主线程中的传值,字符串数组地址
子线程中以(const int& i,char str[])方式接收;在子线程中i并不是主线程的变量,而是复制主线程的变量值
应此可以在传值时,子线程不以引用接收,const int i
对于主线程字符串数组的接收,使用const string& str(将字符串数组转为string)(考虑何时将字符串数组转为string
在主线程创建子线程时:thread t(func,i,string(str)));通过创建string临时对象,让子线程接收临时对象
                                   主线程拷贝一份字符串
//在创建线程时,构造临时对象传递给子线程,(会调用有参构造,后调用拷贝构造)

传基本类型变量:使用值传递

传类,地址等:使用临时对象,用const & 接

若是使用隐式类型转换让子线程接收,则是在子线程中进行对象的创建

若是使用临时对象的模式让子线程接收,则是在主线程中进行对象的创建和拷贝

由于使用的是拷贝的对象,在子线程中修改的变量不会影响原变量(即使使用join,也是调用拷贝构造);

std::ref()若是期望子线程能修改主线程传入的变量,不发生拷贝构造,则使用std::ref()
(func,std::ref(obj));类对象
子线程不用使用const接收,可修改(仍旧传引用)
传智能指针:传的是unique_ptr<int> p(new int(100));的唯一指针
唯一指针,使用移动语义转化std::move(p)传参  要使用join()
接收unique_ptr<int> pan
使用任意类成员函数创建线程(成员函数指针)
class A
{
   void thread_work(int num){}//任意一个线程执行成员函数
};
int main()
{
 A a;
 std::thread myt(&A::thread_work,a,15);//a可以用(std::ref()= =&a)减少一次拷贝构造
 myt.join();
}
线程创建(类的函数地址,类的具体对象,使用该函数的参数)
多线程创建
#include<iostream>
using namespace std;
#include<vector>
#include<thread>
#include<algorithm>
void func(int a)
{
    cout << "线程执行" << a << endl;
}
int main()
{
    vector<thread> v;
    for (int i = 0; i < 10; i++)
    {
        v.emplace_back(func,i);//创建线程对象,执行函数为func
    }
    for (auto iter = v.begin(); iter != v.end(); ++iter)
    {
        iter->join();
    }
    cout << "主线程" << endl;
    return 0;
}
多线程对共享数据的访问

1.共享数据,写者与读者问题:在class中包装,把共享资源放到private里,把类成员函数作为线程执行的入口函数

有的成员函数作为读共享资源的线程函数

有的成员函数作为写共享资源的线程函数

使用类实例化对象,有一个共享资源,用类成员函数开启线程,使用同一个对象,资源共享

互斥量(mutex)保护共享资源

mutex 类型

#include<mutex>
//同读者写者问题:在共享资源里定义互斥锁  
std::mutex m_mutex;
m_mutex.lock();//对临界资源加锁
m_mutex.unlock();//对临界资源解锁
lock_guard避免死锁 lock_guard类模板
对临界资源加锁后,可使用
std::lock_guard<std::mutex> locka(a.mutex,std::adopt_lock);//只负责加锁与解锁锁,避免忘记释放锁造成死锁

lock_guard取代了lock(),unlock() ,使用lock_guard后不能再使用lock()和unlock();在构造函数里lock(),析构函数里unlock()

在共享的资源class里

对资源使用前:
std::lock_guard<std::mutex> m_lock_guard(m_mutex);//加互斥变量m_mutex锁
即可
在其定义的作用域结束后释放

可以使用lock_guard避免死锁

死锁问题
std::lock(a.mutex,b.mutex,...);//要锁住对象的锁
std::lock_guard<std::mutex> locka(a.mutex,std::adopt_lock);//只负责释放a.mutex锁

要把两个及以上的锁锁上:

使用std::lock(a.mutex,b.mutex);锁上两个,a.unlock(),b.unlock();释放两个锁

std::lock(a.mutex,b.mutex,...);//要锁住对象的锁,紧接着
std::lock_guard<std::mutex> locka(m_mutex1,std::adopt_lock);//只负责释放a.mutex锁
std::lock_guard<std::mutex> lockb(m_mutex2,std::adopt_lock);

由lock_guard负责释放锁 std::adopt_lock是结构体对象,起标记作用:表示mutex已经 lock了,不需再lock_guard里再lock()

unique_lock
  1. 类模板(加锁后可以实时解锁)

  2. 可以使用unique_lock替代lock_guard,第二个参数也可以使用std::adopt_lock,相同含义

  3. std::try_to_lock尝试去获取mutex锁,在尝试获取该锁前,不能对mutex加锁;不然可能会加锁两次

    std::unique_lock<std::mutex> m_lock(m_mutex,std::try_to_lock);
    if(m_lock.owns_lock())//拿到了锁
    else//没拿到
    
  4. std::defer_lock 前提不能先lock; 初始化一个没有加锁的mutex

    可以使用unique_lock的成员函数:
    示例:
    std::unique_lock<std::mutex> sb(m_mutex,std::defer_lock);//初始化没有加锁的mutex
    sb.lock();//手动加锁,不用解锁
    sb.unlock();//解锁,lock会自动加锁,unlock增加灵活性(只有lock才能unlock)
    sb.try_lock();//尝试加锁,加锁成功返回true,失败返回false
    sb.release();//返回所管理的mutex对象指针,并释放所有权;unique_lock和m_mutex不再有关系
    mutex对象指针:std::mutex* ptr=sb.release();
    

unique_lock与mutex绑定,其拥有mutex的所有权,可以转移mutex的所有权(不能复制)

std::unique_lock<std::mutex> sb2(std::move(sb));//移动语义(右值引用)sb指向空
返回局部的std::unique_lock<std::mutex>
std::unique_lock<std::mutex> temp_unique_lock()
{
  std::unique_lock<std::mutex> temp(m_mutex);
  return temp;//存在寄存器中,右值;以右值引用的方式将其转为左值
  //unique_lock的移动构造函数
}
std::unique_lock<std::mutex> sbb=temp_unique_lock();
std::call_once(),保证一个函数只被调用一次
  1. 第二个参数是要保证的函数名
  2. 需要与一个标记结合使用,(std::once_flag)( 一个结构)
  3. call_once通过这个标记决定对应的函数是否执行,调用call_once成功后,call_once就把该标记设为“已调用”;后续再调用call_once(),只要once_flag为“已调用”,对应的函数不再执行
std::once_flag m_flag;//全局

std::call_once(m_flag,function_name);//调用,function_name只能执行一次
条件变量 同步信号 std::condition_variable wait() notify_one()

与互斥量配合

在资源里声明该条件变量

std::mutex m_mutex;
std::condition_variable m_cond;//条件变量

/*m_mutex与m_lock绑定了,wait需绑定m_lock
  如果lambda表达式的返回值是true,wait()不阻塞,继续执行下面的代码
  若是返回false,wait()将解锁互斥量,并阻塞到本行
  如果没有第二个lambda参数,wait解锁互斥量,直接阻塞到本行
  阻塞后要等到其他线程notify_one解放
*/
std::unique_lock<std::mutex> m_lock(m_mutex);
m_cond.wait(m_lock,[this](){
//等待队列(条件变量)作为调用主体,在某线程中使用该方法,线程(符合条件)就阻塞到该等待队列,配合互斥锁
  if(?) return true;
  return false;
})
//唤醒:唤醒某个等待队列cond,阻塞到其队列的线程被唤醒:
  std::unique_lock<std::mutex> m_lock(m_mutex);
  m_cond.notify_one();//紧接着把锁释放;

当wait()被唤醒后,其又不断尝试获取互斥锁

若是wait()有第二个lambda参数,若是为false;其又释放互斥锁,并阻塞

wait()成功返回,继续下面的代码,此时互斥锁是被锁着的

notify_one()通知一个线程,notify_all()通知所有wait()在该等待队列的线程

std::async std::future创建后台任务并返回值

std::async 是一个函数模板,用来启动一个异步任务,启动一个异步任务后,返回一个std::future对象,std::future是一个类模板

启动一个异步任务:创建一个线程并开始执行对应的线程入口函数,返回一个std::future对象,这个std::future对象里边含有线程入口函数所返回的结果(线程返回结果);通过调用future对象的成员函数get()获取线程返回结果 get()只能调用一次

#include<future>
int thread_func()//线程执行入口函数,返回(int)10
{
return 10;
}
int main()
{
   std::future<int> p=std::async(thread_func);//启动一个线程,执行函数thread_func,返回结果存到future里<int>与返回结果类型一致
   p.get();//使用该对象的get()获取结果;线程执行结束后才能调用get();get()不拿到值会阻塞
}

p.wait();只等待线程返回,不返回结果 wait()、get()主线程会阻塞

若是类成员函数做入口函数:

class A{
int thread_fun(int num);
}
int main()
{
A a;
int temp=19;
std::future<int> p=std::async(&A::thread_fun,&a,temp);
}

向std::async()传递参数std::launch(枚举类型),达到一些目的

std::launch::deferred:线程入口函数被延迟到只有在调用future的wait() get()时才调用

不使用wait() get()不会调用线程入口函数(即使后面调用wait()或get(),也没创建新线程,线程函数在主线程中运行)

std::future<int> p=std::async(std::launch::deferred,thread_func);

std::launch::async,在调用async时就开始立即创建线程

async 默认的参数是(std::launch::async | std::launch::deferred) 在创建时可能调用前者,也可能调用后者(系统决定是异步(创建新线程)还是同步)

std::packaged_task(打包任务)
  1. 类模板,模板参数是各种可调用对象(线程入口函数)通过std::packaged_task把各种可调用对象包装起来

    int thread_fun(int num) {}
    int main()
    {
      std::packaged_task<int(int)> p(thread_fun);//<int(int)>是线程入口函数的返回值类型,传参类型;(thread_fun)是入口函数名
      std::thread t1(std::ref(p),1);//将打包的对象传给线程启动的构造函数,1表示线程启动函数thread_fun要传的形参
      t1.join();
      std::future<int> result=p.get_future();//future里包含packsged里线程执行结果
      result.get();
    }
    
  2. std::packaged_task<int(int)> p([](int num){
    //lambda表达式
    });
    
  3.  std::packaged_task<int(int)> p(thread_fun);
     packaged_task可以直接调用
     
     p(10);// 10为函数参数
     std::future<int> res=p.get_future();
     <<res.get();// 线程返回值
    
  4. 通过std::packaged_task把各种可调用对象(可创建线程的函数)包装起来

vector<std::packaged_task<int(int)> > vec;//把包装的包放到容器里
vec.push_back(std::move(p));//p是前面的对象
vec.emplace_back(thread_func);//或

//取出来也用移动语义:
auto iter=vec.begin();
std::packaged_task<int(int)> k=std::move(*iter);//可以删除第一个元素,迭代器失效了
std::promise 类模板(获取另一个线程保存的值)
  1. 在一个线程中给promise对象赋值,可以在其他线程中取出该值

    void thread_func(std::promise<int>& temp,int cal)
    {
     //运算
     temp.set_value(cal);//把运算的结果存到传进来的promise对象里保存
     return;
    }
    int main()
    {
    std::promise<int> p;
    std::thread t1(thread_func,std::ref(p),18);
    t1.join();
    //获取t1线程保存的值
    std::future<int> ful=p.get_future();//promise与future绑定 用于获取线程保存的值
    auto item=ful.get();
    }
    
  2. 也可以把future ful传给线程2获取使用线程1的值std::ref(ful)

    void thread_func2(std::future<int>& temp){}
    
    std::thread t2(thread_func2,std::ref(ful));
    
    
std::future的其他成员函数

get(),wait()阻塞等待

  1. 枚举类型
std::future<int> p=std::async(thread_func);//启动一个线程,执行函数
//其他成员函数
std::future_status status=p.wait_for(std::chrono::second(1));//等待线程执行1秒,若是线程执行1秒后还不返回结果,则超时
if(status==std::future_status::timeout)//超时
{
  //...
}
else if(status==std::future_status::ready)//没超时 
{}else if(status==std::future_status::deferred)//线程延迟执行
//线程延迟执行:async(std::launch::deferred,thread_func);//子线程在主线程执行
std::shared_future
  1. 在 future里,只能使用一次get()获取线程值,因为get()是移动语义,会将原future对象值移动到要存的临时变量上;原future对象是空,再次使用出错

  2. 若是有多个线程要通过get()获取future里的值;使用std::shared_future

  3. std::shared_future,是类模板;主要使用是把原future对象传给shared_future

     std::packaged_task<int(int)> p(thread_fun);
     packaged_task可以直接调用
     
     p(10);// 10为函数参数
     std::future<int> res=p.get_future();
     std::shared_future<int> newres(std::move(res));//即可get()多次
     
    
std::atomic原子操作 类模板

对于一段代码,可以使用互斥量来达到排队访问

对于一个变量,可以使用原子操作,原子操作比互斥量效率更高

std::atomic<int> m_count=0;//变量m_count是原子

对其操作按常规操作即可

一般的原子操作针对于++、--、+=、-= 、&=等基本操作

使用a=a+1就不支持原子操作

std::async继续

async 默认的参数是(std::launch::async | std::launch::deferred) 在创建时可能调用前者,也可能调用后者(系统决定是异步(创建新线程)还是同步)

系统决定是异步(创建新线程)还是同步:

  1. std::thread创建线程,如果系统资源紧张,创建失败,程序崩溃

  2. 如果用std::async创建异步任务,一般不会崩溃;若系统资源紧张无法创建新线程,std::async这种不加额外参数的调用,就不会创建新线程;后续谁调用了get()函数,请求结果,这个(异)同步任务就运行在执行这条get()语句所在的线程上。

  3. 如果使用std::launch::async创建新线程,系统资源紧张就会崩溃。

  4. 使用默认的async,判断系统到时候是创建异步任务还是同步任务:使用wait_for()判断

    future接收async(fun):
    std::future_status s=futu.wait_for(std::chrono::seconds(6));
    if(s==std::future_status::deferred)//等判断是选择何种创建方式
    
递归加锁 递归的独占互斥量recursive_mutex

mutex 多次lock会出错(独占互斥量)

允许同一个线程,同一个互斥量多次被锁lock()

std::recursive_mutex m_mutex;//递归的独占互斥量
std::lock_guard<std::recursive_mutex> sb(m_mutex);//可多次加锁
带超时的互斥量
  1. std::timed_mutex
    函数1:try_lock_for(..):参数是一段时间,在等待的一段时间里,如果拿到锁,就继续走,如果在这段时间里拿不到锁,同样不阻塞,执行后面的代码
    函数2:try_lock_until():参数是一个未来的时间点,在这个未来时间点还未到来之前,如果拿到了锁,就执行里面的代码;若是时间点到了还未拿到锁,也不阻塞,继续执行下面的代码
    
    std::timed_mutex m_mutes;
    
    
    std::chrono::milliseconds timeout(100);
    if(m_mutex.try_lock_for(timeout))//在这时间段内尝试获得锁,拿到则返回true
    //if(m_mutex.try_lock_until(chrono::steady_clock::now()+timeout))//未来时间点到来前尝试获得锁
    {
    //拿到了锁
    }else{
    //时间段内没拿到锁,继续执行
    }
    
  2. std::recursive_timed_mutex  带超时的递归的独占互斥量;基本同std::timed_mutex一样
    
虚假唤醒

一个wait()可能被多次使用notify_one()唤醒,可能在wait()被唤醒后临界区没有数据使用

虚假唤醒:wait()第二个参数使用lambda通过判断是否位空,返回true false

std::atomic以原子的方式读写

用一个原子类初始化另一个原子类 不允许使用拷贝构造的方式初始化

应使用:

atomic<int> atm2(atm1.load());//读atm1的值,初始化atm2
atm2.store(12);//以原子的方式将12写入atm2
c++ 基类中若是有纯虚函数,基类中也应有纯虚析构函数
若是基类中没有纯虚析构函数,基类若是以共享指针指向派生类对象,最后基类的析构函数会调用派生类的析构函数
删除vector里特定的所有值
vector.erase(std::remove(vector.begin(),vector.end(),value),vector.end());
remove把所有的value放到vector后面,并且返回所有符合的第一个迭代器

标签:std,函数,记录,int,lock,零碎,线程,c++,mutex
From: https://www.cnblogs.com/persistencejunjie/p/17486414.html

相关文章

  • 2023-06-16:给你一份工作时间表 hours,上面记录着某一位员工每天的工作小时数。 我们认
    2023-06-16:给你一份工作时间表hours,上面记录着某一位员工每天的工作小时数。我们认为当员工一天中的工作小时数大于8小时的时候,那么这一天就是「劳累的一天」。所谓「表现良好的时间段」,意味在这段时间内,「劳累的天数」是严格大于「不劳累的天数」。请你返回「表现良好时间段」......
  • 【React工作记录一百零八】前端小知识点扫盲笔记记录9
    前言我是歌谣放弃很容易但是坚持一定很酷微信公众号关注前端小歌谣带你进入前端巅峰交流群今天继续对前端知识的小结如何截取字符串<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metahttp-equiv="X-UA-Compatible"content="IE=edge">......
  • 【React工作记录一百零九】前端小知识点扫盲笔记记录10
    前言我是歌谣放弃很容易但是坚持一定很酷微信公众号关注前端小歌谣带你进入前端巅峰交流群今天继续对前端知识的小结对称数<!DOCTYPEhtml><htmllang="en"> <head> <metacharset="UTF-8"/> <metahttp-equiv="X-UA-Compatible"content="IE=edge"/>......
  • 对C++中const的说明
    对C++中const的说明在C++中,const是一个关键字,用于指定对象或变量是只读的,即不可修改。它可以应用于不同的上下文中,包括:对象和变量声明:通过在变量或对象的声明前加上const关键字,可以将其标记为只读。这意味着一旦被初始化,就不能再修改该对象或变量的值。constintx=10;//声明一......
  • C++ multi process share value via write and read data from serialized file,the b
    #include<atomic>#include<chrono>#include<cmath>#include<condition_variable>#include<cstddef>#include<forward_list>#include<fstream>#include<functional>#include<future>#include<iom......
  • 记录--设计一个可选择不连续的时间范围的日期选择器
    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助npm包:sta-datepicker效果图 需求普通的时间选择器要么只能单选,要么只能选范围,不可以随意选择若干个时间,同时大多数现成的时间选择器选择结束会收起来,很不方便。现在需求如下1、可以自己控制展开收起2、可......
  • 过滤实现条件查询记录:
    publicList<TbRemouldAirVO>airQueryByMap(List<TbRemouldAirVO>airVO,Map<String,String>map){//1.改造时间if(!StringUtils.isEmpty(map.get("remouldTimeAir"))){airVO=airVO.stream().filter(x->Objects.equals......
  • 关于vue2路由跳转问题记录
    1.vue路由间跳转和新开窗口的方式(query,params)路由间跳转配置:query方式:参数会在url中显示this.$router.push({path:'路由地址',query:{msg:'helloworld'}})params方式:传参数据不会在导航栏中显示,需要配合路由的name属性使用。this.$......
  • vscode+cmake c++ hello world!
    1.新建一个测试目录hello及一些必要文件D:\HELLO\HELLOCPP│CMakeLists.txt└─main.cppCMakeLists.txt#工程名project(Hello)#生成目标add_executable(Hellomain.cpp)hello.cpp#include<iostream>usingnamespacestd;intmain(){cout<<"hellowo......
  • C/C++航空客运订票管理系统[2023-06-16]
    C/C++航空客运订票管理系统[2023-06-16]用c++设计一个航空客运订票管理系统。系统功能要求如下:(1)查询航线:根据旅客提出的站名输出下列信息:航班号、飞机号、飞行日期(含详细时间段),余票量、已定票乘客的信息;(2)排序功能:根据不同属性对航线进行排序;(3)订票业务:根据客户提出的要求(航班......