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 >
- 【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代替
- 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>
- 基本互斥类:
mutex _name;
- 构造函数:mutex不允许拷贝构造,也不允许move拷贝,最初产生的mutex对象处于unlocked状态
- lock():调用线程将锁住该互斥量,线程调用该函数会发生以下3种情况:
(1) 如果该互斥量当前没有被锁住,则调用线程将该互斥量锁住,直到调用unlock之前,该线程一直拥有该锁。
(2) 如果当前互斥量被其他线程锁住,则当前的调用线程被阻塞住。
(3) 如果当前互斥量被当前调用线程锁住,则会产生死锁,,也就是说同一个线程中不允许锁两次。 - try_lock():尝试锁住互斥量,如果互斥量被其他线程占有,则当前线程也不会被阻塞,线程调用该函数会出现下面3种情况:
(1) 如果当前互斥量没有被其他线程占有,则该线程锁住互斥量,直到该线程调用unlock释放互斥量。
(2) 如果当前互斥量被其他线程锁住,则当前调用线程返回false,而并不会被阻塞掉。
(3) 如果当前互斥量被当前调用线程锁住,则会产生死锁。
- 递归互斥类【能进行多次锁定而不造成死锁】:
recursive_mutex
- 与mutex类似,但是能够进行多次lock,能够规避一些死锁问题
- 定时互斥类【可以锁定一定的时间】:
time_mutex
- 定时递归互斥类:
recursive_timed_mutex;
互斥量死锁
lock_guard
: c++标准库互斥量封装类,用于保护共享数据,防止多个线程同时访问统一资源而导致的数据竞争- 构造函数调用时,该互斥量自动锁定
- 析构函数调用时,该互斥量自动解锁
例:lock_guard< Template > lock_name(obj);
- lock_guard类是 non-copyable的。
unique_lock
: 可以对互斥量进行更加灵活管理,包括延迟加锁、条件变量、超时等- 成员函数:
(1) lock() 尝试对互斥量加锁,若加锁失败则阻塞直到成功加锁
(2) try_lock() 尝试枷锁,成功返回true,失败返回false
- 成员函数:
condition_variable及其使用场景
- 头文件
#include <condition_variable>
- 条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待条件变量的条件成立而挂起;另一个线程使条件成立(给出条件成立信号)。为了防止竞争,条件变量的使用总是和一个互斥量结合在一起。【常见用途:生产者-消费者模型】
- 从条件变量的作用可以知道,在使用条件变量时,分为两个方面:
(1) 用于通知已阻塞线程,共享变量已改变
(2) 用于阻塞某一线程,直至该线程被唤醒
- 创建:
condition_variable cv_name;
cv_obj.wait(unique_lock_obj,arg)
消费者在共享资源不足时,通过wait函数等待,第二个参数arg为true时不等待,false时阻塞等待。
当前线程调用wait()后将被阻塞,直到另外某个线程调用notify_唤醒当前线程;当线程被阻塞时,该函数会自动调用std::mutex的unlock()释放锁,使得其它被阻塞在锁竞争上的线程得以继续执行。一旦当前线程获得通知(notify,通常是另外某个线程调用notify_唤醒了当前线程),wait()函数也是自动调用std::mutex的lock()。wait分为无条件被阻塞和带条件的被阻塞两种。
cv_obj.notify_one()
唤醒某个等待(wait)线程。如果当前没有等待线程,则该函数什么也不做,如果同时存在多个等待线程,则唤醒某个线程是不确定的cv_obj.notify_all()
唤醒所有等待进程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
。