首页 > 其他分享 >面试题之GIL全局解释器锁和互斥锁(详细后面补)

面试题之GIL全局解释器锁和互斥锁(详细后面补)

时间:2023-05-10 18:45:26浏览次数:30  
标签:面试题 Python 解释器 互斥 死锁 线程 GIL

目录

一、全局解释器锁(GIL)

什么是(GIL)全局解释器锁?详细概念

GIL(全局解释器锁)是Python解释器的一个特性。它是一种线程锁,它确保同一时刻只有一个线程可以执行Python字节码。这意味着Python解释器中的多个线程不能同时执行Python代码,而只能交替执行。
GIL的存在是为了保护Python解释器内部的数据结构免受并发修改的影响。由于Python中的一些数据结构(如内存管理器)是非线程安全的,因此如果多个线程同时修改这些结构,可能会导致数据结构出现不一致的状态,甚至导致崩溃。通过使用GIL,Python解释器可以避免这种情况发生,但是这也意味着在多线程Python程序中,只有一个线程可以执行Python字节码,其他线程必须等待。
需要注意的是,GIL只是针对Python解释器内部的线程安全问题而设计的,并不是为了防止多线程Python程序的执行。Python程序可以使用多个线程来并发执行I/O操作或调用C语言编写的扩展模块等任务,这些任务不需要GIL的保护,因此不会被GIL所限制。

1、什么是全局解释器锁

每个CPU在同一时间只能执行一个线程,那么其他的线程就必须等待该线程的全局解释器,使用权消失后才能使用全局解释器,即使多个线程直接不会相互影响在同一个进程下也只有一个线程使用cpu,这样的机制称为全局解释器锁(GIL)。

GIL的设计简化了CPython的实现,使的对象模型包括关键的内建类型,如:字典等,都是隐含的,可以并发访问的,锁住全局解释器使得比较容易的实现对多线程的支持,但也损失了多处理器主机的并行计算能力。

2、全局解释器锁的好处

  1. 避免了大量的加锁解锁的好处

  2. 使数据更加安全,解决多线程间的数据完整性和状态同步

全局解释器的缺点

多核处理器退化成单核处理器,只能并发不能并行。

4、GIL的作用:

多线程情况下必须存在资源的竞争,GIL是为了保证在解释器级别的线程唯一使用共享资源(cpu)。

什么是互斥锁?

概念

互斥锁(Mutex,互斥量)是一种用于保护共享资源(如内存、文件等)的锁,它能够确保在同一时刻只有一个线程可以访问被保护的资源,从而避免多个线程同时访问共享资源导致的数据不一致性和竞态条件等问题。
互斥锁的工作原理是,当一个线程想要访问共享资源时,它必须首先获取互斥锁,如果互斥锁已经被另一个线程持有,则该线程将被阻塞直到互斥锁被释放。当该线程完成对共享资源的访问后,它必须释放互斥锁,以允许其他线程访问共享资源。
在Python中,可以使用threading模块提供的Lock对象来实现互斥锁。使用Lock对象时,可以通过调用acquire()方法来获取锁,如果锁已经被另一个线程持有,则调用线程将被阻塞,直到锁被释放。调用release()方法可以释放锁,允许其他线程获取锁并访问共享资源。需要注意的是,在使用互斥锁时,需要小心避免死锁等问题。

代码解释

# 互斥锁:建议只加载操作数据的部分 否则整个程序的效率会极低
from multiprocessing import Process, Lock
import time
import json
import random


def search(name):
    with open(r'data.json', 'r', encoding='utf8') as f:
        data = json.load(f)
    print('%s查看票 目前剩余:%s' % (name, data.get('ticket_num')))


def buy(name):
    # 先查询票数
    with open(r'data.json', 'r', encoding='utf8') as f:
        data = json.load(f)
    # 模拟网络延迟
    time.sleep(random.randint(1, 3))
    # 买票
    if data.get('ticket_num') > 0:
        with open(r'data.json', 'w', encoding='utf8') as f:
            data['ticket_num'] -= 1
            json.dump(data, f)
        print('%s 买票成功' % name)
    else:
        print('%s 买票失败 非常可怜 没车回去了!!!' % name)


def run(name, mutex):
    search(name)
    mutex.acquire()  # 抢锁
    buy(name)
    mutex.release()  # 释放锁


if __name__ == '__main__':
    mutex = Lock()  # 产生一把锁
    for i in range(10):
        p = Process(target=run, args=('用户%s号' % i, mutex))
        p.start()
"""
锁有很多种 但是作用都一样
 行锁 表锁 ...
"""

二、同步锁

1、什么是同步锁?

同一时刻的一个进程下的一个线程只能使用一个cpu,要确保这个线程下的程序在一段时间内被cpu执,那么就要用到同步锁。

2、为什么用同步锁?

因为有可能当一个线程在使用cpu时,该线程下的程序可能会遇到io操作,那么cpu就会切到别的线程上去,这样就有可能会影响到该程序结果的完整性。

3、怎么使用同步锁?

只需要在对公共数据的操作前后加上上锁和释放锁的操作即可。

4、同步锁的所用:

为了保证解释器级别下的自己编写的程序唯一使用共享资源产生了同步锁。

三、死锁

1、什么是死锁?

指两个或两个以上的线程或进程在执行程序的过程中,因争夺资源或者程序推进顺序不当而相互等待的一个现象。

2、死锁产生的必要条件?

互斥条件、请求和保持条件、不剥夺条件、环路等待条件

3、处理死锁的基本方法?

预防死锁、避免死锁(银行家算法)、检测死锁(资源分配)、解除死锁:剥夺资源、撤销进程

了解的锁

四、递归锁

在Python中为了支持同一个线程中多次请求同一资源,Python提供了可重入锁。这个RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源。递归锁分为可递归锁与非递归锁。

五、乐观锁

假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。

六、悲观锁

假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。

python常用的加锁方式:互斥锁、可重入锁、迭代死锁、互相调用死锁、自旋锁。

1.线程和进程:

线程是属于进程的,线程运行在进程空间内,同一进程所产生的线程共享同一内存空间,当进程退出时该进程所产生的线程都会被强制退出并清除。线程可与属于同一进程的其它线程共享进程所拥有的全部资源,但是其本身基本上不拥有系统资源,只拥有一点在运行中必不可少的信息(如程序计数器、一组寄存器和栈)。

2.线程、进程与协程:

线程和进程的操作是由程序触发系统接口,最后的执行者是系统;协程的操作则是程序员

协程存在的意义:对于多线程应用,CPU通过切片的方式来切换线程间的执行,线程切换时需要耗时(保持状态,下次继续)。协程,则只使用一个线程,在一个线程中规定某个代码块执行顺序。

协程的适用场景: 当程序中存在大量不需要CPU的操作时(IO),适用于协程;

标签:面试题,Python,解释器,互斥,死锁,线程,GIL
From: https://www.cnblogs.com/yuezongke/p/17388968.html

相关文章

  • 百度最新面试题集锦
    1、实现一个函数,对一个正整数n,算得到1需要的最少操作次数。操作规则为:如果n为偶数,将其除以2;如果n为奇数,可以加1或减1;一直处理下去。例子:func(7)=4,可以证明最少需要4次运算n=7n-16n/23n-12n/21要求:实现函数(实现尽可能高效)intfunc(unsignintn);n为输入,返回最小的运算次......
  • 【前端】-近期面试题
    setupsetup中的代码与普通的 <script> 只在组件被首次引入的时候执行一次不同,<scriptsetup> 中的代码会在每次组件实例被创建的时候执行。所以,任何在 <scriptsetup> 声明的顶层的绑定(包括变量,函数声明,以及import导入的内容)都能在模板中直接使用。有方法通过impo......
  • 一道Promise面试题,并对比向其代码中添加await关键字后的变化
    标准代码:(function(){console.log(1);window.setTimeout(()=>{console.log(2);},100);newPromise((resolve)=>{console.log(3);resolve();}).then(()=>{console.log(4);......
  • 面试题之数据库存储引擎
    目录复习老师讲得什么是存储引擎?如何查看常见存储引擎的方式?需要了解的四个存储引擎了解不同存储引擎底层文件个数补充了解详细概念数据库引擎的类型1、InnoDB引擎2、ISAM引擎3、MYISAM引擎4、MEMORY存储引擎5、HEAP引擎6、ARCHIVE引擎7、BERKLEYDB引擎InnoDB与MyISAM差别ACID复......
  • 前端面试题-常见的水平垂直居中实现方案
    方案一:<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metahttp-equiv="X-UA-Compatible"content="IE=edge"><metaname="viewport"content="w......
  • #yyds干货盘点# LeetCode面试题:不同的二叉搜索树 II
    1.简述:给你一个整数 n ,请你生成并返回所有由 n 个节点组成且节点值从 1 到 n 互不相同的不同 二叉搜索树 。可以按 任意顺序 返回答案。 示例1:输入:n=3输出:[[1,null,2,null,3],[1,null,3,2],[2,1,3],[3,1,null,null,2],[3,2,null,1]]示例2:输入:n=1输出:[[1]]2.代......
  • Java后端真实、靠谱、强大的面试题网站:面试梯
    ​ 本文分享一个给力的Java后端面试题网站:面试梯。网址:https://offer.skyofit.com这套题真实、高频、全面、有详细答案、保你稳过面试,让你成为offer收割机。题目包括:Java基础、多线程、JVM、数据库、Redis、Shiro、Spring、SpringBoot、MyBatis、MQ、ELK、分布式、SpringCloud......
  • Mysql面试题
    1.Mysql基础1、from子句组装来自不同数据源的数据;2、where子句基于指定的条件对记录行进行筛选;3、groupby子句将数据划分为多个分组;4、使用聚集函数进行计算;5、使用having子句筛选分组;6、计算所有的表达式;7、select的字段;8、使用orderby对结果集进行排序。SQL语言不同......
  • Python实操面试题
    1、一行代码实现1--100之和#利用sum()函数求和sum(range(1,101))2、如何在一个函数内部修改全局变量#利用global在函数声明修改全局变量a=5deffunc(): globalaa=10func()print(a)#结果:103、列出5个python标准库'''os:提供了不少与操作系统......
  • Django面试题
    1.DjangoORM查询中select_related和prefetch_related的区别??defselect_related(self,*fields)性能相关:表之间进行join连表操作,一次性获取关联的数据。总结:1.select_related主要针一对一和多对一关系进行优化。2.select_related使用SQL的JOIN语句进行......