首页 > 编程语言 >c++多线程编程

c++多线程编程

时间:2024-03-02 18:34:35浏览次数:27  
标签:std 调用 name 编程 c++ 互斥 线程 多线程 函数

c++线程库:<thread>

创建线程:需要可调用的函数或者函数对象作为线程入口点

    例:std::thread threadname ( function_name , args...)   
  • 在C++中,当使用std::thread创建线程并传递类的成员函数时,需要使用&来获取成员函数的地址,同时还需要传递对象的指针(或引用)作为第一个参数。

例:(A为一个类,a为A的一个实例化对象) thread t (&A::func_name,&a,args)

补充:在使用多线程编程时,内存管理变得更加复杂,使用智能指针可以帮助我们更好避免内存泄漏

auto_ptr 是c++ 98定义的智能指针模板,其定义了管理指针的对象,可以将new 获得(直接或间接)的地址赋给这种对象。当对象过期时,其析构函数将使用delete 来释放内存

头文件:#include < memory >

  1. 【c++11已过时】auto_ptr<类型> 变量名(new 类型)

     ·ptr.get()    //获取智能指针托管的指针地址
     ·ptr.release()  //取消智能指针对动态内存的托管
     ·ptr.reset(arg)  //将参数的指针(不指定则为NULL),与托管的指针比较,如果地址不一致,那么就会析构掉原来托管的指针,然后使用参数的指针替代之
  • auto_ptr 主要有三大问题:
     · 复制和赋值会改变资源的所有权,不符合人的直觉。
     · 在 STL 容器中使用auto_ptr存在风险,因为容器内的元素必需支持可复制和可赋值。
     · 不支持对象数组的操作

因此c++11后使用unique_ptr shared_ptr和weak_ptr代替

  1. unique_ptr

函数:

    thread.join();      //执行join后,等待线程完成函数,主线程需要等待子线程运行结束了才可以结束 
    
    thread.detach();    //执行detach可以分离子线程,主线程不会等待子线程运行结束才结束
    
    thread.joinable();  //会返回一个bool值,判断是否已经使用过join或者detach *已调用时返回false*
    
    std::ref(para);         //thread的方法传递引用的时候,必须外层用ref来进行引用传递,否则会编译出错

调用类内私有成员函数:友元函数

例: void func_name();需要调用类内的私有成员函数
则在类内需要通过声明 friend void func_name();

c++11 互斥量

  • 头文件:#include <mutex>
  1. 基本互斥类: mutex _name;
    • 构造函数:mutex不允许拷贝构造,也不允许move拷贝,最初产生的mutex对象处于unlocked状态
    • lock():调用线程将锁住该互斥量,线程调用该函数会发生以下3种情况:
      (1) 如果该互斥量当前没有被锁住,则调用线程将该互斥量锁住,直到调用unlock之前,该线程一直拥有该锁。
      (2) 如果当前互斥量被其他线程锁住,则当前的调用线程被阻塞住。
      (3) 如果当前互斥量被当前调用线程锁住,则会产生死锁,,也就是说同一个线程中不允许锁两次。
    • try_lock():尝试锁住互斥量,如果互斥量被其他线程占有,则当前线程也不会被阻塞,线程调用该函数会出现下面3种情况:
      (1) 如果当前互斥量没有被其他线程占有,则该线程锁住互斥量,直到该线程调用unlock释放互斥量。
      (2) 如果当前互斥量被其他线程锁住,则当前调用线程返回false,而并不会被阻塞掉。
      (3) 如果当前互斥量被当前调用线程锁住,则会产生死锁。
  2. 递归互斥类【能进行多次锁定而不造成死锁】: recursive_mutex
    • 与mutex类似,但是能够进行多次lock,能够规避一些死锁问题
  3. 定时互斥类【可以锁定一定的时间】:time_mutex
  4. 定时递归互斥类:recursive_timed_mutex;

互斥量死锁

  1. lock_guard: c++标准库互斥量封装类,用于保护共享数据,防止多个线程同时访问统一资源而导致的数据竞争
    • 构造函数调用时,该互斥量自动锁定
    • 析构函数调用时,该互斥量自动解锁

例:lock_guard< Template > lock_name(obj);

  • lock_guard类是 non-copyable的。
  1. unique_lock: 可以对互斥量进行更加灵活管理,包括延迟加锁、条件变量、超时等
    • 成员函数:
      (1) lock() 尝试对互斥量加锁,若加锁失败则阻塞直到成功加锁
      (2) try_lock() 尝试枷锁,成功返回true,失败返回false

condition_variable及其使用场景

  • 头文件#include <condition_variable>
  • 条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待条件变量的条件成立而挂起;另一个线程使条件成立(给出条件成立信号)。为了防止竞争,条件变量的使用总是和一个互斥量结合在一起。【常见用途:生产者-消费者模型】
  • 从条件变量的作用可以知道,在使用条件变量时,分为两个方面:
    (1) 用于通知已阻塞线程,共享变量已改变
    (2) 用于阻塞某一线程,直至该线程被唤醒
  1. 创建:condition_variable cv_name;
  2. cv_obj.wait(unique_lock_obj,arg) 消费者在共享资源不足时,通过wait函数等待,第二个参数arg为true时不等待,false时阻塞等待。

当前线程调用wait()后将被阻塞,直到另外某个线程调用notify_唤醒当前线程;当线程被阻塞时,该函数会自动调用std::mutex的unlock()释放锁,使得其它被阻塞在锁竞争上的线程得以继续执行。一旦当前线程获得通知(notify,通常是另外某个线程调用notify_唤醒了当前线程),wait()函数也是自动调用std::mutex的lock()。wait分为无条件被阻塞和带条件的被阻塞两种。

  1. cv_obj.notify_one() 唤醒某个等待(wait)线程。如果当前没有等待线程,则该函数什么也不做,如果同时存在多个等待线程,则唤醒某个线程是不确定的
  2. cv_obj.notify_all() 唤醒所有等待进程
  3. notify_all_at_thread_exit 当调用该函数的线程退出时,所有在 cond 条件变量上等待的线程都会收到通知

condition_variable_any 介绍
与 condition_variable 类似,只不过 condition_variable_any 的 wait 函数可以接受任何 lockable 参数,而condition_variable 只能接受 unique_lock< mutex > 类型的参数,除此以外,和 :condition_variable 几乎完全一样。

异步并发

1. async、future

  • 头文件: #include <future>
  • c++11引入的函数模板,用于异步执行一个函数,并返回到future对象,表示异步操作的结果。使用async可以方便进行异步编程,避免了手动创建和管理线程的麻烦

例:std::future< template > future_name = std::async(std::launch::async, func_name);

2. packaged_task

  • 类模板,用于将一个可调用对象(如函数、函数对象或Lambda表达式)封装为一个异步操作,并返回一个future对象,表示异步操作结果。package_task可以方便将一个函数或可调用对象转换为一个异步操作,供其他线程使用

例: std::packaged_task<returntype()> task_name(func_name);
// 此时得到的是封装好的task对象,需要通过创建线程执行 【记得使用move(task)把可移动对象放入线程执行】
auto result = task_name.get_future();

3. promise

  • 用于在线程中产生一个值,并在另一个线程中获取该值。通常与future和async一起使用,实现异步编程。
  • 创建:std::promise<tmplate> name
  • 获取值: name.get_future();+result.get()

原子操作:atomic

  • c++11标准库中的一个模板类,用于实现多线程编程环境下的原子操作。提供一种线程安全的方式访问和修改共享变量,可以避免多线程环境中的数据竞争问题
  • 头文件: #include <atomic>
  • 创建: std::atomic<template> data_name
  • 相较于加锁实现的互斥,在线程安全中,原子操作的性能更好
  • 常见std::atomic操作:
    load(): 将std::atomic变量的值加载到当前线程的本地缓存中并返回
    store(value): 将value的值存储到std::atomic变量中,这个操作是原子性的
    exchange(value): 将value的值存储到std::atomic变量中,并返回原先的值
    compare_exchange_weak(expected, val)compare_exchange_strong(expected, val):比较 std::atomic 变量的值和 expected 的值是否相同,如果相同,则将 val 的值存储到 std::atomic 变量中,并返回 true;否则,将 std::atomic 变量的值存储到 expected 中,并返回 false

标签:std,调用,name,编程,c++,互斥,线程,多线程,函数
From: https://www.cnblogs.com/self-propelled/p/18049026

相关文章

  • C++ 继承
    1//基类2classAnimal{3  //eat()函数4  //sleep()函数5};678//派生类9classDog:publicAnimal{10  //bark()函数11};基类&派生类一个类可以派生自多个类,这意味着,它可以从多个基类继承数据和函数。定义一个派生类,我......
  • C++ 类的静态成员
    原文可以使用 static 关键字来把类成员定义为静态的。当我们声明类的成员为静态时,这意味着无论创建多少个类的对象,静态成员都只有一个副本。静态成员在类的所有对象中是共享的。如果不存在其他的初始化语句,在创建第一个对象时,所有的静态数据都会被初始化为零。我们不能把静态......
  • C++ 指向类的指针
    原文一个指向C++类的指针与指向结构的指针类似,访问指向类的指针的成员,需要使用成员访问运算符 ->,就像访问指向结构的指针一样。与所有的指针一样,在使用指针之前,对指针进行初始化。在C++中,指向类的指针指向一个类的对象,与普通的指针相似,指向类的指针可以用于访问对象的成员......
  • C++ 拷贝构造函数(初学有点难理解)
    拷贝构造函数是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象。拷贝构造函数通常用于:通过使用另一个同类型的对象来初始化新创建的对象。复制对象把它作为参数传递给函数。复制对象,并从函数返回这个对象。如果在类中没有定义拷......
  • C++填坑系列——新手写代码易错点
    c++新手写代码的几个易错点学习自https://www.youtube.com/watch?v=i_wDa2AS_8w1.限制usingnamespacestd的作用范围如果你自己定义了一个和std空间内同名的函数,当你把std放到globalnamespace(也就是直接usingnamespacestd)中,就会出现函数冲突;usingnamespacestd的使......
  • C++填坑系列——万能引用+移动语义+完美转发
    模板编程中的万能引用、移动语义、完美转发万能引用:T&&,辅助模板编程,这样左值和右值的参数都可以接收;移动语义:std::move,转换为右值,也可结合移动构造函数和移动赋值使用;完美转发:std::forward,可以保留参数的左值和右值属性,因为后续使用该参数可能还需要这个属性;万能引用万能引......
  • C++填坑系列——EffectiveModernC++之类型推导
    接下来会记录我在学习《EffectiveModernC++》的一些总结和思考。鉴于C++的知识太多了,我难以全面覆盖到学习,所以这里借此来补充和发散自己的学习心得:)以下内容由学习这个网站EffectiveModernC++的中文翻译内容得来https://cntransgroup.github.io/EffectiveModernCppChine......
  • C++填坑系列——EffectiveModernC++之新手怎么进入现代C++
    新手怎么进入现代C++1.使用auto来自动推导变量类型2.使用{}来创建变量和对象3.使用nullptr来创建空指针4.使用using代替typedef进行别名定义5.使用enumclass代替enum进行枚举定义6.使用=delete来禁止调用一个函数7.使用override来修饰继承链中的重写函数8.使用co......
  • C++填坑系列——EffectiveModernC++之特殊成员函数
    Chapter移步现代c++之特殊成员函数Item17:Understandspecialmemberfunctiongeneration总结:有必要了解各个函数什么时候自动生成;自动生成的函数有可能产生预期外的行为;特殊成员函数(编译器自动生成):默认构造函数,析构函数,拷贝构造函数,拷贝赋值运算符,移动构造函数,移动赋......
  • C++ 多线程笔记2 线程同步
    C++多线程笔记2线程同步并发(Concurrency)和并行(Parallelism)并发是指在单核CPU上,通过时间片轮转的方式,让多个任务看起来像是同时进行的。实际上,CPU在一个时间段内只会处理一个任务,但是由于切换时间非常快,用户感觉像是多个任务同时在进行。这种方式的优点是可以充分利用CPU资源,......