锁-GIL-同步异步-event

一堆锁

死锁

对同一把互斥锁多次执行acquire 将导致死锁------>资源被占用一直得不到释放,导致其他资源进入阻塞状况

产生死锁的情况:

? 1:对同一把互斥锁,枷锁了多次

? 2:一个共享资源要访问必须具备多把锁,但是这些锁被不同线程或进程持有,就会导致相互等待对方释放资源,从而程序卡死

解决情况:

? 1:抢锁,按照相同循序去抢

? 2:给抢锁,加上超时,若超时则放弃执行

给acquire加上超时,可以保证线程不会卡死

l=Lock()
l.acquire()
l.acquire(timeout=3)

递归锁(了解)

其实和普通锁区别

相同点:多线程之间有互斥效果

不同点:同一线程可以对这个锁执行多次acquire

同一线程必须保证,加锁的次数和解锁的次数相同,其他线程才能抢到这把锁

信号量(了解)

可以限制同时并发执行公共代码的线程数量

如果限制数量为一,则和普通锁没有区别

from threading import Semaphore ,currentThread,Thread
import time
s= Semaphore(5)
def task():
    s.acquire()
    time.sleep(1)
    print(currentThread().name)
    s.release()
for i in range(10):
    Thread(target=task).start()

Thread-1
Thread-2
Thread-3
Thread-5
Thread-4

Thread-6
Thread-7
Thread-8
Thread-9
Thread-10

信号量不是用来解决安全问题的,而是用于限制最大的并发量

GIL

什么是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的内存管理不是线程安全的。(然而,自从吉尔
在现有的基础上,其他功能已经逐渐依赖于它所实施的保证。
"""

为何用这把锁

线程安全问题具体的表现

cpython 解释器 与python程序之间联系

python程序的本质就是一堆字符串,所提运行一个python程序时,必须要开启一个解释器

但是在一个python程序中解释器只有一个,所有代码都要交给他来执行

但是当多个线程都要执行代码时就会产生线程安全问题

cpython的解释器与Gc的问题

python会自动帮我们处理垃圾,清扫垃圾,清扫垃圾也是一对代码,也需要开启一个线程来执行

换句话说就算程序没有开启线程,内部也有多个线程

GC线程与我们程序中的线程就会产生安全问题

申请一块空的内存,再把数据装进去,最后把引用计数加1

如果进行到第二步时候,cpu切换到了GC线程,Gc就会把这个只当作垃圾清理掉

带来的问题

Gil是一把互斥锁,互斥锁将导致效率降低

具体表现在 CPython即便开启了多线程,而且CPU也是多核的,却无法并行执行任务

因为解释器就一个,同一时间只有一个任务在执行

解决方案

么有办法解决,只能避免GLC锁影响我们的效率

1:使用多进程能够实现并行,从而更好地利用多核Cpu

2:对任务进行区分

? 分为两类:

? 1计算密集型 基本没有io 大部分时间都在计算,例如人脸识别,图像处理

? 由于多线程不能并行,应该使用多进程,将任务分给不同的CPU核心

? 2io密集型 计算任务非常少的,大部分时间都在等待IO操作

? 由于网络IO速度对比cpu处理速度非常慢,多线程并不会造成太大影想

? 另外如有大量的客户端链接服务,进程根本开不起来,只能用多线程

关于性能的讨论

加锁是为了解决线程的安全问题

再来,由于有了锁,导致CPython中的多线程不能并行只能并发

1,python是一门语言,GIl时Cpython解释器的问题,还有jpython pypy

2,如果是单核cpu,gil不会造成任何影响

3,由于目前大多数程序是基于网络的,网络的速度对比cpu是非常慢的,导致即使多核cpu也无法提高效率

4,对于io密集型任务,不会有太大的影响

5,如果没有这把锁,程序员之恩自己解决安全问题

from multiprocessing import Process
from threading import  Thread
import time
# # 计算密集型任务
#
# def task():
#     for i in range(100000000):
#         1+1
#
#
# if __name__ == '__main__':
#     start_time = time.time()
#
#     ps = []
#     for i in range(5):
#         p = Process(target=task)
#         # p = Thread(target=task)
#         p.start()
#         ps.append(p)
#
#     for i in ps:i.join()
#
#     print("共耗时:",time.time()-start_time)

# 多进程胜

def task():
    for i in range(100):
        with open(r"1.死锁现象.py",encoding="utf-8") as f:
            f.read()
if __name__ == '__main__':
    start_time = time.time()

    ps = []
    for i in range(10):
        p = Process(target=task)
        # p = Thread(target=task)
        p.start()
        ps.append(p)

    for i in ps:i.join()
    print("共耗时:",time.time()-start_time)

    共耗时: 0.05649399757385254

GIL与自定义锁的区别

GIL锁住的是解释器级别的数据

自定义锁,锁的是解释器以外的共享资源,例如硬盘上的文件,控制台

对于这种不属于解释器的数据资源就是应该给自己枷锁处理

线程池与进程池

池表示容器

线程就是装线程的容器

为什么要装在容器当中呢?

1,可以避免频繁的创建和销毁(进程/线程)的资源的开销

2,可以限制同时存在的线程数量,以保证服务器不会因为资源不足导致奔溃

3,帮忙管理了线程的生命周期

4,管理了任务的分配

import os
import time
from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
from threading import activeCount,enumerate,currentThread

# 创建一个线程池   指定最多可以容纳两个线程
pool = ThreadPoolExecutor(20)

def task():
    print(currentThread().name)

# 提交任务到池子中
pool.submit(task)
pool.submit(task)

print(enumerate())

ThreadPoolExecutor-0_0

ThreadPoolExecutor-0_0

[<_MainThread(MainThread, started 4667078080)>, <Thread(ThreadPoolExecutor-0_0, started daemon 123145511550976)>, <Thread(ThreadPoolExecutor-0_1, started daemon 123145516806144)>]

#进程池使用
def task():
    time.sleep(1)
    print(os.getpid())

if __name__ == '__main__':
    pool = ProcessPoolExecutor(2)
    pool.submit(task)
    pool.submit(task)
    pool.submit(task)

2365

2366

2365

若进程不结束,池子里面的进程或线程 也一直是活的

同步 异步——————

程序的状态》》》 阻塞非阻塞

处理任务的方式》》》 并发 并行 串行

同步:提交任务后必须在原地等待,知道任务结束

异步:提交任务不需要在原地等待,可以继续往下执行代码

? 异步的效率高于同步,异步任务将导致一个问题,---任务的发起方不知道任务何时处理完毕

异步同步指的是提交任务的方式

解决方案:

1轮番询问:重复一段时间问一次---->效率低,无法及时获取结果

2让任务的执行方主动通知(异步回调)---->可以及时拿到任务的结果(推荐)

异步回调使用案例:

# 异步回调
from threading import Thread
# 具体的任务
def task(callback):
    print("run")
    for i in range(100000000):
        1+1
    callback("ok")

#回调函数 参数为任务的结果
def finished(res):
    print("任务完成!",res)

print("start")
t = Thread(target=task,args=(finished,))
t.start()  #执行task时 没有导致主线程卡主 而是继续运行
print("over")

线程池中的回调使用:

# 使用案例:
def task(num):
    time.sleep(1)
    print(num)
    return "hello python"

def callback(obj):
    print(obj.result())

pool = ThreadPoolExecutor()
res = pool.submit(task,123)
res.add_done_callback(callback)
print("over") 

event事件

线程间状态同步

你把一个任务丢到了子线程中,这个任务将异步执行,如何得到这个任务的执行状态

执行状态和执行结果不是一个概念

如果需要拿到,执行结果可以采用异步回调

假设

一个线程 负责启动服务器 启动服务器需要花一定的时间

另一个线程作为客户端 要连接服务器 必须保证服务器已经启动

要获取状态可以采永轮训的方法 但是浪费了CPU资源 而且可能会造成延迟 不能立即获取状态

就可以使用事件来完成状态同步

事件本质就是 一个标志 可以是False 或是True

特殊之处在于 其包含一个wait函数 可以阻塞当前线程 直到状态从False变为True

from threading import  Thread,Event
import time

# is_boot = False
e = Event()

def start_server():
    # global is_boot
    print("starting server......")
    time.sleep(3)
    print("server started!")
    # is_boot = True
    # 修改事件的值为True
    e.set()

def connect_server():
    e.wait() # 等待事件从False 变为true
    if e.is_set():
        print("连接服务器成功!")
    # while True:
    #     if is_boot:
    #         print("连接服务器成功!")
    #         break
    #     else:
    #         print("失败 服务器未启动!")
    #     time.sleep(0.5)

t1 = Thread(target=start_server)

t2 = Thread(target=connect_server)

t1.start()
t2.start()

starting server......
server started!
连接服务器成功!

原文地址:https://www.cnblogs.com/zhuyuanying123--/p/11140624.html

时间: 2024-11-08 08:47:57

锁-GIL-同步异步-event的相关文章

并发编程--一堆锁,GIL,同步异步,Event事件

目录 一堆锁 死锁现象(*****) 递归锁 RLock (了解) 信号量 (了解) GIL(*****) 什么时GIL锁 为什么需要GIL锁 Cpython解释器与GC的问题 GIL锁带来的问题 多线程与多进程性能对比 进程池与线程池 同步异步(*****) Event事件 一堆锁 死锁现象(*****) ? 死锁指的是,某个资源被占用之后,一直得不到释放,导致其他需要这个资源的线程进入阻塞状态 产生死锁的情况 对同一把互斥锁,进行了多次加锁 一个共享资源,在访问时必须具备多把锁,但是这些锁被

并发&amp;并行 同步&amp;异步 GIL 任务 同步锁 死锁 递归锁

# 并发&并行 同步&异步 GIL 任务 同步锁 死锁 递归锁 # 并发:是指系统具有处理多个任务(动作)的能力 # 并行:是指系统具有 同时 处理多个任务(动作)的能力 # 同步:当进程执行到一个IO(等待外部数据)的时候,需要等待外部数据接收完 # 异步:当进程执行到一个IO(等待外部数据)的时候,不需要等待外部数据接收完,还可以做其它的处理 # GIL: 全局解释器锁 在python中,无论你启多少个线程,你有多少个cpu,python在执行的时候在同一时刻只请允许一个线程运行 #

python GIL锁、进程池与线程池、同步异步

一.GIL全局解释器锁 全局解释器锁 在CPython中,全局解释器锁(GIL)是一个互斥锁,它可以防止多个本机线程同时执行Python代码.之所以需要这个锁,主要是因为CPython的内存管理不是线程安全的.(然而,自从GIL存在以来,其他特性已经逐渐依赖于它所执行的保证) 什么是GIL 全局解释器锁, 施加在解释器上的互斥锁 为什么需要GIL 由于CPython的内存管理时非线程安全,于是CPython就给解释器加上锁, 解决了安全问题. GIL的加锁与解锁时机 加锁的时机: 在调用解释器时

GIL锁,线程池,同步异步

1.GIL 是一个全局解释器锁 ,是一种互斥锁 为什么需要GIL:因为一个python.exe进程中只有一分解释器,如果这个进程开启了多个线程都要执行代码 多线程之间要竞争解释器,一旦竞争就有可能出现问题 带来的问题:同一时间只有一个线程可以访问解释器 好处:保证了多线程的数据完全 thread-safe 线程安全的 多个线程同时访问也不会出问题 not thread-safe 非线程安全的 多个线程同时访问可能会出问题 (加锁) 默认情况下一个进程只有一个线程 是不会出现问题的 ,但是不要忘记

python第三十七天,GIL全局解释器锁*****,线程池与进程池 同步异步,阻塞与非阻塞,异步回调

GIL全局解释器锁 1.什么是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

python37 1.GIL--全局解释器锁 2.GIL带来的问题 3.为什么需要GIL 4.GIL的加锁解锁时机 5.关于GIL的性能的讨论 6.线程常用方法 7.GIL锁与自定义锁的区别 8.进程池与线程池 9.同步异步 10.异步调用

复习1.JoinableQueue--可以被join的队列2.多线程3线程的使用方法与进程一模一样3.1守护线程3.2线程安全问题3.3解决方案3.3.1互斥锁mutex3.3.2递归锁Rlock3.3.3信号量semaphore3.3.4死锁问题 详解:1.JoinableQueue--可以被join的队列 1.1join是等待任务结束 队列怎么叫结束 调用task_done一次则表示有一个数据被处理完成了,当task_done次数等于put的次数就意味着任务处理完成了 1.2这就是join的

python-并发并行、同步异步、同步锁

并发:系统具有处理多个任务(动作)的能力 并行:系统具有同时处理多个任务(动作)的能力 同步:当进程执行到一个IO(等待外部数据)的时候,需要等待,等待即同步 异步:当进程执行到一个IO(等待外部数据)的时候,不需要等待,待数据接收成功后,再回来处理. GIL:全局解释锁:无论你有多少个线程,你有多少个CPU,Python在执行的时候会淡定的在同一时刻只允许一个线程运行.(解释器层面保护进程安全) GIL的作用:同一时刻,只有一个线程被CPU在执行,造成单线程运行结果,多核用不到. 垃圾回收机制

python 同步异步,并发并行,同步锁

并发:系统具有处理多个任务(动作)的能力 并行:系统具有同时处理多个任务(动作)的能力 同步:当进程执行到一个IO(等待外部数据)的时候,需要等待,等待即同步 异步:当进程执行到一个IO(等待外部数据)的时候,不需要等待,待数据接收成功后,再回来处理. GIL:全局解释锁:无论你有多少个线程,你有多少个CPU,Python在执行的时候会淡定的在同一时刻只允许一个线程运行.(解释器层面保护进程安全) GIL的作用:同一时刻,只有一个线程被CPU在执行,造成单线程运行结果,多核用不到. 垃圾回收机制

GIL 线程池 进程池 同步 异步

1.GIL(理论 重点)2.线程池 进程池3.同步 异步 GIL 是一个全局解释器锁,是一个互斥锁 为了防止竞争解释器资源而产生的 为何需要gil:因为一个python.exe进程中只有一份解释器,如果这个进程开启了多个线程 都要执行代码 多线程之间要竞争解释器 一旦竞争就有可能出现问题 带来的问题:同一时间只有一个线程可以访问解释器 好处:保证了多线程的数据安全 thread-safe 线程安全的 多个线程同时访问也不会出问题 not thread-safe 非线程安全的 多个线程同时访问可能