全局解释器锁

本文详细探讨了Python中GIL(全局解释器锁)的作用及其对多线程的影响,分析了多线程在计算密集型和IO密集型任务中的表现,并介绍了递归锁、信号量、事件和队列等高级同步机制。

全局解释器锁

GIL(全局解释器锁)
在CPython解释器才有GIL的概念,不是python的特点
GIL也是一把互斥锁
将并发变成串行 牺牲了效率但是提高了数据的安全
ps:
1.针对不同的数据 应该使用不同的锁去处理
2.自己不要轻易的处理锁的问题 哪怕你知道acquire和release
当业务逻辑稍微复杂的一点情况下 极容易造成死锁
CPython中的GIL的存在是因为python的内存管理不是线程安全的

内存管理
引用计数:值与变量的绑定关系的个数
标记清除:当内存快要满的时候 会自动停止程序的运行 检测所有的变量与值的绑定关系
给没有绑定关系的值打上标记,最后一次性清除
分代回收:(垃圾回收机制也是需要消耗资源的,而正常一个程序的运行内部会使用到很多变量与值
并且有一部分类似于常量,减少垃圾回收消耗的时间,应该对变量与值的绑定关系做一个分类
) 新生代(5S)》》》青春代(10s)》》》老年代(20s)
垃圾回收机制扫描一定次数发现关系还在,会将该对关系移至下一代
随着代数的递增 扫描频率是降低的

同一个进程下的多个线程能否同时运行
GIL类似于是加在解释器上面的一把锁

 

计算密集型

from threading import Thread
import os,time
def work():
    res=0
    for i in range(100000000):
        res*=i
​
​
if __name__ == '__main__':
    l=[]
    print(os.cpu_count())  # 本机为6核
    start=time.time()
    for i in range(6):
        # p=Process(target=work) #耗时  4.732933044433594
        p=Thread(target=work) #耗时 22.83087730407715
        l.append(p)
        p.start()
    for p in l:
        p.join()
    stop=time.time()
    print('run time is %s' %(stop-start))

io密集型

from multiprocessing import Process
from threading import Thread
import threading
import os,time
def work():
    time.sleep(2)
​
​
if __name__ == '__main__':
    l=[]
    print(os.cpu_count()) #本机为6核
    start=time.time()
    for i in range(4000):
        p=Process(target=work) #耗时9.001083612442017s多,大部分时间耗费在创建进程上
        # p=Thread(target=work) #耗时2.051966667175293s多
        l.append(p)
        p.start()
    for p in l:
        p.join()
    stop=time.time()
    print('run time is %s' %(stop-start))

python的多线程到底有没有用 需要看情况而定 并且肯定是有用的

多进程+多线程配合使用

死锁

递归锁
可以被第一个抢到该锁的人多次的acquire和release,内部会有一个计数
acquire就是加
release就是减
当别人在抢这把锁的时候 只要计数不为零 永远也别想抢到

 

mutexA = mutexB = RLock()  # A B现在是同一把锁
​
​
class MyThread(Thread):
    def run(self):  # 创建线程自动触发run方法 run方法内调用func1 func2相当于也是自动触发
        self.func1()
        self.func2()
​
    def func1(self):
        mutexA.acquire()
        print('%s抢到了A锁'%self.name)  # self.name等价于current_thread().name
        mutexB.acquire()
        print('%s抢到了B锁'%self.name)
        mutexB.release()
        print('%s释放了B锁'%self.name)
        mutexA.release()
        print('%s释放了A锁'%self.name)
​
    def func2(self):
        mutexB.acquire()
        print('%s抢到了B锁'%self.name)
        time.sleep(1)
        mutexA.acquire()
        print('%s抢到了A锁' % self.name)
        mutexA.release()
        print('%s释放了A锁' % self.name)
        mutexB.release()
        print('%s释放了B锁' % self.name)
​
for i in range(10):
    t = MyThread()
    t.start()

只要类加括号实例化对象 无论传入的参数是否一样生成的对象肯定不一样 单例模式除外

自己千万不要轻易的处理锁的问题

信号量

如果互斥锁是单个卫生间
那么信号量就是多个卫生间

 

from threading import Semaphore,Thread
import time
import random
​
​
sm = Semaphore(5)  # 造了一个含有五个的坑位的公共厕所
​
def task(name):
    sm.acquire()
    print('%s占了一个坑位'%name)
    time.sleep(random.randint(1,3))
    sm.release()
​
for i in range(40):
    t = Thread(target=task,args=(i,))
    t.start()

event事件

event事件
e = Event()

e.wait() 等待信号

e.set() 发送信号

from threading import Event,Thread
import time
​
# 先生成一个event对象
e = Event()
​
​
def light():
    print('红灯正亮着')
    time.sleep(3)
    e.set()  # 发信号
    print('绿灯亮了')
​
def car(name):
    print('%s正在等红灯'%name)
    e.wait()  # 等待信号
    print('%s加油门飙车了'%name)
​
t = Thread(target=light)
t.start()
​
for i in range(10):
    t = Thread(target=car,args=('伞兵%s'%i,))
    t.start()

线程queue

队列 Queue
堆栈 LifoQueue
优先级 P...Queue
只有一个参数 是元组的形式
数字越小优先级越高

import queue
"""
同一个进程下的多个线程本来就是数据共享 为什么还要用队列
​
因为队列是管道+锁  使用队列你就不需要自己手动操作锁的问题 
​
因为锁操作的不好极容易产生死锁现象
"""
​
​
​
# q = queue.Queue()
# q.put('hahha')
# print(q.get())
​
​
# q = queue.LifoQueue()
# q.put(1)
# q.put(2)
# q.put(3)
# print(q.get())
​
​
# q = queue.PriorityQueue()
# # 数字越小 优先级越高
# q.put((10,'haha'))
# q.put((100,'hehehe'))
# q.put((0,'xxxx'))
# q.put((-10,'yyyy'))
# print(q.get())

  

 

转载于:https://www.cnblogs.com/cherish937426/p/11351829.html

Python全局解释器锁(GIL)限制了同一时刻只有一个线程可以执行Python字节码,在多线程编程中会影响性能。以下是几种不使用GIL或绕过其限制的方法: ### 使用多进程代替多线程 多进程编程允许多个Python解释器实例并行运行,每个进程都有自己独立的GIL,因此可以充分利用多核CPU的优势。Python的`multiprocessing`模块可以实现多进程编程。 ```python import multiprocessing def worker(num): """每个进程执行的任务""" print(f'Worker {num} started') result = num * num print(f'Worker {num} finished with result: {result}') if __name__ == '__main__': processes = [] for i in range(4): p = multiprocessing.Process(target=worker, args=(i,)) processes.append(p) p.start() for p in processes: p.join() ``` ### 使用Cython编写扩展 Cython是Python的超集,它允许在Python代码中嵌入C代码。通过使用Cython编写扩展,可以绕过GIL的限制。在Cython中,可以使用`with nogil`语句来释放GIL。 ```python # example.pyx def calculate(): cdef int i, result = 0 # 释放GIL with nogil: for i in range(1000000): result += i return result ``` 然后使用`setup.py`文件进行编译: ```python # setup.py from distutils.core import setup from Cython.Build import cythonize setup( name='Example', ext_modules=cythonize('example.pyx'), ) ``` 最后在终端中运行`python setup.py build_ext --inplace`进行编译。 ### 使用NumPy等库 NumPy等科学计算库的底层是用C或Fortran实现的,在执行这些库的函数时,GIL会被释放,从而实现并行计算。 ```python import numpy as np # 创建一个大数组 arr = np.random.rand(1000000) # 对数组进行计算 result = np.sum(arr) print(result) ``` ### 使用异步编程 异步编程通过`asyncio`库实现,它基于单线程和事件循环,不依赖于多线程,因此不受GIL的影响。 ```python import asyncio async def task(): print('Task started') await asyncio.sleep(1) print('Task finished') async def main(): tasks = [task() for _ in range(4)] await asyncio.gather(*tasks) asyncio.run(main()) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值