某日二师兄参加XXX科技公司的C++工程师开发岗位第31面:

面试官:什么是锁?有什么作用?

二师兄:在C++中,锁(Lock)是一种同步工具,用于保护共享资源,防止多个线程同时访问,从而避免数据竞争和不一致。

面试官:有哪些锁?

二师兄:从种类上分,可以分为普通锁、读写锁、递归锁等种类。

二师兄:从实现上分,可以分为互斥锁、自旋锁、信号量、条件变量等。

面试官:互斥锁如何使用?

二师兄:在C++11之前,C++便准层面并没有定义锁,锁的应用要依赖于平台。Linux下使用pthread库中的mutex

#include <pthread.h>
pthread_mutex_t mutex_ = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_lock(&mutex_);
//被保护的区域
pthread_mutex_unlock(&mutex_);

二师兄:C++11引入了std::mutex,统一了各个平台上互斥锁的使用:

#include <mutex>
std::mutex mutex_;
mutex_.lock();
//被保护的区域
mutex_.unlock();

面试官:pthread_mutexstd::mutex有没有非阻塞的api

二师兄:有的,分别是pthread_mutex_trylock()try_lock(),当获取不到锁时这两者并不阻塞当前线程,而是立即返回。需要注意的是,当pthread_mutex_trylock()获取到锁时返回0,而std::mutex::try_lock()方法获取不到锁时返回false

面试官:std::lock_guardstd::unique_lock用过吗?

二师兄:用过。

面试官:两者有什么相同点和不同点?

二师兄:相同点是两者都使用RAII(资源获取即初始化)技术实现的锁,支持自动上锁,自动解锁。

二师兄:不同点主要包括三个方面:

1.灵活性:std::unqiue_lock的灵活性要高于std::lock_guradstd::unique_lock可以在任何时间解锁和锁定,而std::lock_guard在构造时锁定,在析构时解锁,不能手动控制。

2.所有权:std::unique_lock支持所有权转移,而std::lock_gurad不支持。

3.性能:由于std::unique_lock的灵活性更高,它的性能可能会稍微低一些。

面试官:能实现一个lock_gurad吗?

二师兄:我尝试一下:

class lock_guard
{
explicit lock_guard(std::mutex& m):mutex_(m)
{
mutex_.lock();
}
~lock_guard()
{
mutex_unlock();
}
private:
std::mutex& mutex_;
};

面试官:为什么会发生死锁?

二师兄:当进程A持有锁1请求锁2,进程B持有锁2请求锁1时,两者都不会释放自己的锁,两者都需要对方的锁,就会造成死锁。当然现实中可能比这要复杂,但原理是相同的。

面试官:如何避免死锁?

二师兄:主要从以下几个方面入手:

1.避免循环等待,如果需要在业务中获取不同的锁,保证所有业务按照相同的顺序获取锁。

2.使用超时锁,当锁超时时,自动释放锁。

3.使用try_lock,当锁被占用时,返回false并继续执行。

4.锁的粒度尽量要小,只保护竟态数据而不是整个流程。

面试官:知道adopt_lock_t/defer_lock_t/try_to_lock_t这三种类型的用法吗?

二师兄:额。。不知道。。

面试官:好的,回去等通知吧。

让我们来看看最后一个问题:

知道adopt_lock_t/defer_lock_t/try_to_lock_t这三种类型的用法吗?

adopt_lock_t/defer_lock_t/try_to_lock_t都是空类,主要表示std::lock_guradstd::unqiue_lock的默认构造中的操作:

adopt_lock_t:默认互斥量已被当前线程锁定,不使用lock()方法对互斥量加锁:

std::mutex mtx_;
mtx_.lock(); //lock
{
std::lock_guard<std::mutex> lock_(mtx_,std::adopt_lock); //这里默认当前线程已经对mtx_加过锁
...
}//unlock

defer_lock_t:虽然我拥有了std::mutex的引用,但是在构造函数中并不调用lock()方法对互斥量加锁:

std::mutex mtx_;
{
std::unique_lock<std::mutex> ulock_(mtx_,std::defer_lock); //这里并没有加锁
ulock_.lock();
if(ulock_.owns_lock())
{
//locked
}else
{
//unlocked
}
}//if locked,unlock

try_to_lock_t:在构造函数执行是并不是使用lock()方法加锁,而是使用try_lock()方法加锁:

std::mutex mtx_;
{
std::unique_lock<std::mutex> ulock_(mtx_,std::try_to_lock); //这里mtx_如果没有被锁定,则加锁成功,否则加锁失败
if(ulock_.owns_lock())
{
//locked
}else
{
//unlocked
}
}//if locked,unlock

adopt_lock_t可以用于std::lock_guradstd::unique_lock,而defer_lock_t/try_to_lock_t只能用于std::unique_lock

关注我,带你21天“精通”C++!(狗头)

C++面试八股文:如何避免死锁?的更多相关文章

  1. 《面试八股文》之kafka21卷

    微信公众号:moon聊技术 关注选择" 星标 ", 重磅干货,第一 时间送达! [如果你觉得文章对你有帮助,欢迎关注,在看,点赞,转发] 大家好,我是moon,最新一篇面试八股文系 ...

  2. 《面试八股文》之 Redis 16卷

    微信公众号:moon聊技术 关注选择" 星标 ", 重磅干货,第一 时间送达! [如果你觉得文章对你有帮助,欢迎关注,在看,点赞,转发] 大家好,我是 moon. redis 作为 ...

  3. 《面试八股文》之 JVM 20卷

    微信公众号:moon聊技术 关注选择" 星标 ", 重磅干货,第一 时间送达! [如果你觉得文章对你有帮助,欢迎关注,在看,点赞,转发] 大家好,我是 moon. <面试八股 ...

  4. 一天吃透MySQL面试八股文

    什么是MySQL MySQL是一个关系型数据库,它采用表的形式来存储数据.你可以理解成是Excel表格,既然是表的形式存储数据,就有表结构(行和列).行代表每一行数据,列代表该行中的每个值.列上的值是 ...

  5. 一天吃透JVM面试八股文

    什么是JVM? JVM,全称Java Virtual Machine(Java虚拟机),是通过在实际的计算机上仿真模拟各种计算机功能来实现的.由一套字节码指令集.一组寄存器.一个栈.一个垃圾回收堆和一 ...

  6. 这可能是最全面的MySQL面试八股文了

    什么是MySQL MySQL是一个关系型数据库,它采用表的形式来存储数据.你可以理解成是Excel表格,既然是表的形式存储数据,就有表结构(行和列).行代表每一行数据,列代表该行中的每个值.列上的值是 ...

  7. 这可能是最全面的Redis面试八股文了

    Redis连环40问,绝对够全! Redis是什么? Redis(Remote Dictionary Server)是一个使用 C 语言编写的,高性能非关系型的键值对数据库.与传统数据库不同的是,Re ...

  8. 三天吃透Java虚拟机面试八股文

    本文已经收录到Github仓库,该仓库包含计算机基础.Java基础.多线程.JVM.数据库.Redis.Spring.Mybatis.SpringMVC.SpringBoot.分布式.微服务.设计模式 ...

  9. 三天吃透Redis面试八股文

    本文已经收录到Github仓库,该仓库包含计算机基础.Java基础.多线程.JVM.数据库.Redis.Spring.Mybatis.SpringMVC.SpringBoot.分布式.微服务.设计模式 ...

  10. 三天吃透MySQL面试八股文

    本文已经收录到Github仓库,该仓库包含计算机基础.Java基础.多线程.JVM.数据库.Redis.Spring.Mybatis.SpringMVC.SpringBoot.分布式.微服务.设计模式 ...

随机推荐

  1. Express实现定时发送邮件

    在开发中我们有时候需要每隔 一段时间发送一次电子邮件,或者在某个特定的时间进行发送邮件, 无需手动去操作,基于这样的情况下我们需要用到了定时任务,一般可以写个定时器,来完成相应的需求,在 node.j ...

  2. socket搭建web服务端

    import socket from threading import Thread import time def html(conn): time_tag = str(time.time()) p ...

  3. Yii初学者必看-yii 表单验证规则

    对yii深入了解总结出:希望对初学者有些帮助 Active Record (AR) 是一个流行的 对象-关系映射 (ORM) 技术. 每个 AR 类代表一个数据表(或视图),数据表(或视图)的列在 A ...

  4. Android Studio 样式和主题背景

    样式和主题背景 转载自   Styles and Themes  |  Android Developers 借助 Android 中的样式和主题背景,您可以将应用设计的细节与界面的结构和行为分开,其 ...

  5. GraphQL渗透测试详解

    GraphQL介绍 GraphQL概述 GraphQL 是一种查询语言,用于 API 设计和数据交互.它是由 Facebook 发布的一款新型的数据查询和操作语言,自 2012 年起在内部使用,自 2 ...

  6. 安装node并创建vue项目

    1.多版本管理工具 nvm https://github.com/coreybutler/nvm-windows/releases nvm-setup.zip 2. 打开nvm文件夹下的setting ...

  7. Python-psycopg2的简单使用

    一.简介 psycopg2 库是 python 用来操作 postgreSQL 数据库的第三方库. 二.安装 1.执行如下命令安装 pip3 install psycopg2 2.使用Pycharm安 ...

  8. 网络编程之java简易聊天室实现

    最近浅学习了一些关于网络编程方面的知识,视频是跟着狂神学习的,可能学习的不是很深 说到网络,相信大家都对TCP.UDP和HTTP协议这些都不是很陌生,学习这部分应该先对端口.Ip地址这些基础知识有一定 ...

  9. Microsoft Loop初体验

    目前AI copilot无法使用. 问题 图片 在设置中可以打开实验选项 简单开箱使用 很多人说微软的loop竞品是notion,那么作为卡片盒双链笔记软件,最热门的应用当然是notion.从loop ...

  10. Sourcetree 提交顺序

    总结:一共5个步骤 1.首先获取git主分支的代码. 2.暂存所需要上传的代码. 3.拉取代码(如发生文件冲突先暂不处理). 4.提交代码,然后再次拉取代码(不显示冲突跳下一步).如果还是显示文件冲突 ...