C++多线程基础知识

本文详细介绍了C++中的多线程基础知识,包括std::thread的观察器和操作,如get_id、joinable和detach等。此外,还讨论了互斥量std::mutex的使用,如lock、unlock和try_lock函数,以及锁类如std::lock_guard、std::unique_lock和std::shared_lock的应用。还涵盖了std::condition_variable的等待和通知机制,以及信号量和条件变量在同步中的作用。
摘要由CSDN通过智能技术生成

std::thread类成员函数:

一 观察器:

  1. get_id:获取线程ID,返回一个类型为std::thread::id的对象。

  2. joinable:检查线程是否可被join。检查thread对象是否标识一个活动(active)的可行性线程。缺省构造的thread对象、已经完成join的thread对象、已经detach的thread对象都不是joinable。(并不是执行代码中的线程才是活跃,只要和内核关联就是活跃状态)

  3. hardware_concurrency:静态成员函数,返回当前计算机最大的硬件并发线程数目。基本上可以视为处理器的核心数目。 (如果有逻辑内核,返回是是虚拟内核数量)

  4. native_handle:该函数返回与std::thread具体实现相关的线程句柄。

二 操作:
5. join:等待该线程执行完毕,剥夺其资源。

  1. detach:将当前线程对象所代表的执行实例与该线程对象分离,使得线程的执行可以单独进行。一旦线程执行完毕,它所分配的资源将会被释放。

  2. swap:交换两个线程对象所代表的底层句柄。

std::mutex(互斥量) 类成员函数

1、构造函数,std::mutex不允许拷贝构造,也不允许 move 拷贝,最初产生的 mutex 对象是处于 unlocked 状态的。

2、lock():
调用线程将锁住该互斥量。线程调用该函数会发生下面 3 种情况:
(1). 如果该互斥量当前没有被锁住,则调用线程将该互斥量锁住,直到调用 unlock之前,该线程一直拥有该锁。
(2). 如果当前互斥量被其他线程锁住,则当前的调用线程被阻塞住。
(3). 如果当前互斥量被当前调用线程锁住,则会产生死锁(deadlock)。

3、unlock():
解锁,释放对互斥量的所有权。

4、try_lock():
尝试锁住互斥量,如果互斥量被其他线程占有,则当前线程也不会被阻塞。线程调用该函数也会出现下面 3 种情况。
(1). 如果当前互斥量没有被其他线程占有,则该线程锁住互斥量,直到该线程调用 unlock 释放互斥量。
(2). 如果当前互斥量被其他线程锁住,则当前调用线程返回 false,而并不会被阻塞掉。
(3). 如果当前互斥量被当前调用线程锁住,则会产生死锁(deadlock)。

Lock类

1. std::lock_guard(锁保护)
std::lock_gurad 是 C++11 中定义的模板类
template class lock_guard;

lock_guard 对象通常用于管理某个锁(Lock)对象,因此与 Mutex RAII (创建对象时获得资源,析构时释放资源)相关,方便线程对互斥量上锁。
即在某个 lock_guard 对象的声明周期内,它所管理的锁对象会一直保持上锁状态;而 lock_guard 的生命周期结束之后,它所管理的锁对象会被解锁(注:类似 shared_ptr 等智能指针管理动态分配的内存资源)。

2. std::unique_lock(独占)
但是 lock_guard 最大的缺点也是简单,没有给程序员提供足够的灵活度,因此,C++11 标准中定义了另外一个与 Mutex RAII 相关类 unique_lock,该类与 lock_guard 类相似,也很方便线程对互斥量上锁,但它提供了更好的上锁和解锁控制。

unique_lock 对象以独占所有权的方式( unique owership)管理 mutex 对象的上锁和解锁操作,所谓独占所有权,就是没有其他的 unique_lock 对象同时拥有某个 mutex 对象的所有权。

3. scoped_lock(范围)
类 scoped_lock 是提供便利 RAII 风格机制的互斥包装器,它在作用域块的存在期间占有一或多个互斥。创建
scoped_lock 对象时,它试图取得给定互斥的所有权。
控制离开创建 scoped_lock 对象的作用域时,析构 scoped_lock 并以逆序释放互斥。若给出数个互斥,则使用免死
锁算法,如同以 std::lock 。

4. shared_lock(共享)
类 shared_lock 是通用共享互斥所有权包装器,允许延迟锁定、定时锁定和锁所有权的转移。锁定 shared_lock ,
会以共享模式锁定关联的共享互斥( std::unique_lock 可用于以排他性模式锁定)。

std::condition_variable

condition_variable 是同步原语,能用于阻塞一个线程,或同时阻塞多个线程,直至另一个线程修改共享变量(条件)并通知condition_variable。

条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:

  1. 一个线程因为等待“条件变量的条件成立”而挂起;
  2. 另外一个线程使“条件成立”,给出信号,从而唤醒被等待的线程。

利用条件变量实现线程同步必须拥有:
1 g_num (全局变量)
2 mutex (互斥量)
3 condition_variable

condition_variable类的成员函数

  1. notify_one 通知一个等待的线程(唤醒)
  2. notify_all 通知所有等待的线程
  3. wait 阻塞当前线程,直到条件变量被唤醒(先解锁,再挂起,等待条件变量的信号)
  4. wait_for 阻塞当前线程,直到条件变量被唤醒,或到指定时限时长后
  5. wait_until 阻塞当前线程,直到条件变量被唤醒,或直到抵达指定时间点
  6. native_handle 返回原生句柄

信号量

信号量 (semaphore) 是一种轻量的同步原件,用于制约对共享资源的并发访问(控制线程的并发数量)。在可
以使用两者时,信号量能比条件变量更有效率。

counting_semaphore 实现非负资源计数的信号量

binary_semaphore 仅拥有二个状态的信号量

操作

  1. release : 原子地将内部计数器的值增加1,唤醒等待的线程(V操作)
  2. acquire : 内部计数器大于0,则尝试将其减少1,线程继续执行;否则阻塞线程,直至内部计数器大于0,并能成功减少1,线程唤醒继续执行。(P操作)
  3. try_acquire 尝试减少内部计数器而不阻塞
  4. try_acquire_for 尝试减少内部计数器,至多阻塞一段时长
  5. try_acquire_until 尝试减少内部计数器,阻塞直至一个时间点

常量
max 返回内部计数器的最大可能值

使用信号量循环打印 ABC

#include <iostream>
#include <thread>
#include <semaphore>
using namespace std
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值