互斥锁

并发编程-多线程,GIL锁

倾然丶 夕夏残阳落幕 提交于 2020-04-06 11:32:00
本章内容: 1.什么是GIL 2.GIL带来的问题 3.为什么需要GIL 4.关于GIL的性能讨论 5.自定义的线程互斥锁与GIL的区别 6.线程池与进程池 7.同步异步,阻塞非阻塞 一.什么是GIL 官方解释: ''' In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. This lock is necessary mainly because CPython’s memory management is not thread-safe. (However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.) ''' 释义: 在CPython中,这个全局解释器锁,也称为GIL,是一个互斥锁,防止多个线程在同一时间执行Python字节码,这个锁是非常重要的,因为CPython的内存管理非线程安全的,很多其他的特性依赖于GIL,所以即使它影响了程序效率也无法将其直接去除 总结: 在CPython中,GIL会把线程的并行变成串行

Redis 的雪崩、穿透和击穿

梦想的初衷 提交于 2020-04-05 19:50:16
缓存雪崩 假设每天高峰期每秒 5000 个请求,本来缓存在高峰期可以扛住每秒 4000 个请求,但是缓存机器意外发生了全盘宕机。缓存挂了,此时 1 秒 5000 个请求全部落数据库,数据库必然扛不住,它会报一下警,然后就挂了。此时,如果没有采用什么特别的方案来处理这个故障,DBA 很着急,重启数据库,但是数据库立马又被新的流量给打死了。 通常我们在使用 Redis 的时候,都会为缓存设置过期时间,但是如果在某个时间点,有大量缓存失效,那么下一个时间点就会有大量请求访问到数据库,这种情况下,数据库可能因为访问量多大导致“崩溃”,这就是缓存雪崩。 第一种情况:缓存雪崩的事前事中事后的解决方案如下: 事前:redis 高可用,主从+哨兵,redis cluster,避免全盘崩溃。 事中:本地 ehcache 缓存 + hystrix 限流&降级,避免 MySQL 被打死。对于缓存大面积失效的情况可以设置缓存的失效时间使用随机数,避免此问题。 事后:redis 持久化,一旦重启,自动从磁盘上加载数据,快速恢复缓存数据。 第二种情况: 不设置缓存过期时间 最暴力的解决办法,缓存不设置自动过期时间,只要缓存不崩,数据库就不会崩。 设置随机过期时间 另外一个办法,就是让缓存过期时间不那么一致,比如一批缓存数据24小时后过期,那么就在这个基础上,让每条缓存的过期时间前后随机 1-6000 秒(1

go-并发

落爺英雄遲暮 提交于 2020-03-26 18:12:05
并发编程 并发与并行 并发:同一时间段内执行多个任务(交替执行)。 并行:同一时刻执行多个任务(一起执行)。 Go语言的并发通过 goroutine 实现。 goroutine 类似于线程,属于用户态的线程,我们可以根据需要创建成千上万个 goroutine 并发工作。 goroutine 是由Go语言的运行时(runtine)调度完成,而线程是由操作系统调度完成的。 Go语言还提供 channel 在多个 goroutine 间进行通信。 goroutine 和 channel 是 Go 语言秉承的 CSP(Communicating Sequential Process)并发模式的重要实现基础。 goroutine Go语言中的 goroutine 的概念类似于线程,但 goroutine 是由Go的运行时(runtime)调度和管理的。Go程序会智能地将 goroutine 中的任务合理地分配给每个CPU。Go语言之所以被称为现代化的编程语言,就是因为它在语言层面已经内置了调度和上下文切换的机制。 在Go语言编程中你不需要去自己写进程、线程、协程,你的技能包里只有一个技能– goroutine ,当你需要让某个任务并发执行的时候,你只需要把这个任务包装成一个函数,开启一个 goroutine 去执行这个函数就可以了。 Go语言中使用 goroutine 非常简单

GIL全局解释器锁

我只是一个虾纸丫 提交于 2020-03-22 05:11:00
GIL介绍 GIL本质就是一把互斥锁,既然是互斥锁,所有互斥锁的本质都一样,都是将并发运行变成串行,以此来控制同一时间内共享数据只能被一个任务所修改,进而保证数据安全 如果多个线程的target=work,那么执行流程是: 多个线程先访问到解释器的代码,即拿到执行权限,然后将target的代码交给解释器的代码去执行 解释器的代码是所有线程共享的,所以垃圾回收线程也可能访问到解释器的代码而去执行,这就导致了一个问题:对于同一个数据100,可能线程1执行x=100的同时,而垃圾回收执行的是回收100的操作,解决这种问题没有什么高明的方法,就是加锁处理,如下图的GIL,保证python解释器同一时间只能执行一个任务的代码 GIL与Lock GIL 与Lock是两把锁,保护的数据不一样,前者是解释器级别的(当然保护的就是解释器级别的数据,比如垃圾回收的数据),后者是保护用户自己开发的应用程序的数据,很明显GIL不负责这件事,只能用户自定义加锁处理,即Lock,如下图 分析: 1、100个线程去抢GIL锁,即抢执行权限 2、肯定有一个线程先抢到GIL(暂且称为线程1),然后开始执行,一旦执行就会拿到lock.acquire() 3、极有可能线程1还未运行完毕,就有另外一个线程2抢到GIL,然后开始运行,但线程2发现互斥锁lock还未被线程1释放,于是阻塞,被迫交出执行权限,即释放GIL 4

iOS-NSThread多线程

*爱你&永不变心* 提交于 2020-03-20 01:52:35
NSThread 一、创建和启动线程 一个NSThread对象就代表一条线程 创建方法一: 优点:可以对线程进行更多的设置 NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil]; [thread start]; // 线程一启动,就会在线程thread中执行self的run方法 //设置名字 - (void)setName:(NSString *)n; - (NSString *)name; 创建方法二 创建线程后自动启动线程 [NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil]; 或者 //隐式创建并启动线程 [self performSelectorInBackground:@selector(run) withObject:nil]; 上述2种创建线程方式的优缺点 + 优点:简单快捷,不用手动调用start方法, 系统会自动启动 + 缺点:无法对线程进行更详细的设置 二、相关使用方法 主线程相关用法 + (NSThread *)mainThread; // 获得主线程 - (BOOL)isMainThread; // 是否为主线程 +

并发编程的艺术02-过滤锁算法

 ̄綄美尐妖づ 提交于 2020-03-19 20:12:26
3 月,跳不动了?>>> 导读 分析并发计算的实质就是分析时间。有时候希望事件同时发生,有时候希望事件在不同时间发生。— 《多处理器编程的艺术》 本章内容会介绍一种互斥锁算法也就是标题中所说的过滤锁算法,虽然它在实际应用中可能并不实用,但却是一种经典算法。掌握这些算法将会有助于你对并发,互斥,死锁,饥饿的理解。 互斥 mutual exclusion 在多线程环境中 所有线程共享一个共同的时间。一个线程是一个状态机,而它状态的转换称为事件。 事件是瞬时的,它们在单个瞬间发生。 在并发编程中互斥是指在同一时刻,至多只能有一个线程进入临界区内,这样的特性称为互斥 。如果并发执行的程序没有互斥特性,将会无法保障计算结果的正确性。 所以说互斥是一种安全特性 。为了直观的便于大家理解,下面会举一个生活中的例子说明互斥特性。 上图是一辆和谐号动车正在一条铁轨上行驶, 很显然在同一时刻这条铁轨的某一段中最多只能允许一辆列车在上面行驶,这就是互斥特性的体现。如果说在某一时刻这条铁轨的同一段中出现了两辆列车都在行驶,那么就会产生安全问题,这体现了互斥的安全特性 。对上面这个模型进行抽象, 时间是永远不会改变且不会受外界影响的,行驶的和谐号列车可以看作是一个运行的线程,而这条铁轨的某一段可以看作临界区 在并发编程当中与互斥特性密切相关的还有 无死锁 , 无饥饿 两个重要特性。聪明的读者应该已经想到了

【Linux】线程----线程安全

懵懂的女人 提交于 2020-03-17 00:59:28
线程安全 概念 实现方法 互斥 互斥锁 互斥锁原理 互斥锁操作流程: 举例 死锁 死锁产生的必要条件: 预防死锁 避免死锁 同步 条件变量 条件变量提供的接口功能 例子 问题分析 注意 概念 多个执行流 对 临界资源 进行 争抢访问 ,而不会造成 数据二义性 或者 逻辑混乱 ,称这段争抢访问的过程是线程安全的。 实现方法 线程安全的实现: 同步 :通过 条件判断 ,实现对临界资源访问的时序合理性 互斥 :通过 唯一访问 ,实现对临界资源访问的安全性 互斥 互斥的实现技术: 互斥锁 、 信号量 实现互斥的原理:只要保证 同一时间只有一个执行流能够访问资源 就是互斥 对临界资源进行状态标记:没人访问的时候标记为1,表示可以访问;有人正在访问的时候,就标记为0,表示不可访问;在对 临界资源 进行 访问之前 先 进行状态判断 ,决定是否能够访问,不能访问则使其 休眠 。 互斥锁 互斥锁原理 互斥锁:其实就是一个 计数器 ,只有0/1的计数器,用于标记资源当前的访问状态 1----可访问 0----不可访问 互斥锁想要实现互斥,每个线程在访问临界资源之前都要先访问 同一个互斥锁 (加锁);意味着 互斥锁本身就是一个临界资源 (涉及到计数器的修改,修改过程必须保证安全,如果连自己都保护不了,如何保护他人?) 如果是普通的计数器,则操作步骤为 将mutex的值加载到CPU的一个寄存器

什么是信号量?

折月煮酒 提交于 2020-03-11 19:51:12
信号量是一种编程概念,经常用于解决多线程问题。 我对社区的问题: 什么是信号量,如何使用? #1楼 因此,想象每个人都在尝试去洗手间,而洗手间只有一定数量的钥匙。 现在,如果没有足够的键,该人需要等待。 因此,可以将信号量视为代表可用于浴室(系统资源)的那些键集,以供不同进程(浴室行进者)请求访问。 现在想象一下试图同时去洗手间的两个过程。 那不是一个好情况,并且使用信号量来防止这种情况。 不幸的是,信号量是一种自愿机制,过程(我们的洗手间)可以忽略它(即,即使有钥匙,仍然有人可以将门打开)。 二进制/互斥量和计数信号量之间也存在差异。 在 http://www.cs.columbia.edu/~jae/4118/lect/L05-ipc.html上 查看讲义。 #2楼 构建并发程序有两个基本概念-同步和互斥。 我们将看到这两种类型的锁(信号灯通常是一种锁定机制)如何帮助我们实现同步和互斥。 信号量是一种编程结构,可通过实现同步和互斥来帮助我们实现并发。 信号量有两种类型,二进制和计数。 信号量包括两个部分:一个计数器和一个等待访问特定资源的任务列表。 信号量执行两项操作:wait(P)[就像获取锁一样],以及release(V)[类似于释放锁] –这是一个可以对信号量执行的仅有的两项操作。 在二进制信号量中,计数器在逻辑上介于0和1之间。您可以认为它类似于具有两个值的锁:打开

Interview_操作系统_day21

為{幸葍}努か 提交于 2020-03-09 21:45:12
并发和并行 并发: 同一时刻只能运行一条指令。在宏观上看起来是多个程序同时运行,但微观上是多个程序的指令交替着运行的。并发不能提高计算机的性能,只能提高效率。 并行: 同一时刻可以运行多条指令。无论从宏观还是微观,都是一起执行的。比如多核 \(cpu\) ,多个程序分别运行在不同的核上,互不影响。并行确实提高了计算机的效率。 用户态和内核态区别 操作系统两种 \(CPU\) 状态: 核心态:运行操作系统程序 用户态:运行用户程序 操作系统的指令划分: 特权指令:只能由操作系统使用,用户不能使用的指令。 非特权指令:用户程序可以使用的指令。 特权级: \(R0、R1、R2、R3\) \(R0\) 相当于内核态, \(R3\) 相当于用户态 不同的特权级别可以运行不同的指令 区别: 内核态和用户态是操作系统的两种运行级别。用户态拥有最低的特权级,核心态拥有较高的特权级。 处于用户态时,进程能够访问到的内存空间和对象受到限制,其所占有的处理机是可以被抢占的。 处于内核态时,进程能够访问所有的内存空间和对象,且所占有的处理机是不可以被抢占的。 操作系统为什么要分内核态和用户态 为了安全。在 \(cpu\) 中,如果有些指令用错会使系统崩溃,所以用户程序是不可信的,无论程序员是否有意,都可能把系统弄崩溃。 分了内核态和用户态之后,操作系统对内核级别指令进行封装,然后为用户提供系统服务

条件变量实现生产者和消费者

感情迁移 提交于 2020-03-08 22:37:08
条件变量不是锁,但也可以造成线程阻塞,通常与互斥锁配合使用,给多线程提供一个会和的场所 常用函数: int pthread_cond_destroy(pthread_cond_t *cond); int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr); 初始化:包括动态初始化和静态初始化 int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime); int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex); 函数作用: 1.阻塞等待条件变量满足 2.释放已掌握的互斥锁(解锁互斥量) 1 2 两步为原子操作 3.当被唤醒, pthread_cond_wait函数返回时,解除阻塞并重新获取互斥锁 int pthread_cond_broadcast(pthread_cond_t *cond); int pthread_cond_signal