码迷,mamicode.com
首页 > 其他好文 > 详细

GIL、Event事件、信号量、死锁、递归锁

时间:2019-08-14 16:47:01      阅读:86      评论:0      收藏:0      [点我收藏+]

标签:div   数据共享   priority   star   回收机制   rlock   研究   work   假设   

去幕布 >>

GIL全局解释器锁

Python解释器有很多种,最常见的就是CPython解释器
GIL本质也是一把互斥锁:将并发变成串行,牺牲效率保证数据的安全?
用来阻止同一个进程下的 多个线程的 同时执行
(同一个进程内的多个线程无法实现并行,但是可以实现并发)

Python的多线程没法利用多核优势,是不是就没有用了?

GIL的存在是因为CPython解释器的内存管理不是线程安全的

垃圾回收机制:
a. 引用计数
b. 标记清除
c. 分代回收

研究Python的多线程是否有用,需要分情况讨论:

技术图片
(假设有四个任务,每个任务需要10s 处理完成)?

1. 计算密集型?:
单核情况下?:
?开线程更省资源(原因是开进程需要申请内存空间等操作,浪费时间)
多核情况下:
开进程处理    10s
开线程处理    40s


2. I/O密集型:
?????单核情况下:
开线程更省资源(原因和计算密集型的一样)
多核情况下:
???开线程更省资源
?(原因是 I/O秘籍情况下,线程工作方式为 切换+保存
?进程申请内存空间,是要消耗很长时间的,
而?线程的切换+保存基本不消耗时间)
?

python的多线程到底有没有用
需要看情况而定  并且肯定是有用的
多进程+多线程配合使用?
View Code
技术图片
# I/O密集型
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())  # 本机为8核
    start=time.time()
?
    for i in range(400):
        p=Process(target=work)  # 耗时9.506524085998535s多,大部分时间耗费在创建进程上
        # p=Thread(target=work)  # 耗时 2.0459132194519043 s多
        l.append(p)
        p.start()
?
    for p in l:
        p.join()
?
    stop=time.time()
    print(run time is %s %(stop-start))
I/O密集型
技术图片
# 计算密集型
from multiprocessing import Process
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())  # 本机为8核
    start=time.time()
?
    for i in range(8):
        p=Process(target=work)  # 耗时  8.460614204406738s
        # p=Thread(target=work)  # 耗时 34.822699308395386s
        l.append(p)
        p.start()
?
    for p in l:
        p.join()
?
    stop=time.time()
    print(run time is %s %(stop-start))
计算密集型

死锁

1. 代码表示下一步要acquire一个锁
2. ?但是两个线程互相拿着对方需要的锁,于是程序就hang到这儿了
这种情况就是死锁
技术图片
from threading import Thread, Lock, current_thread
import time
?
mutexA = Lock()
mutexB = Lock()??
?
class MyThread(Thread):
?
    # 创建线程自动触发run方法 run方法内调用func1 func2相当于也是自动触发
    def run(self):  
        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()??
?
‘‘‘执行结果:
Thread-1抢到了A锁
Thread-1抢到了B锁
Thread-1释放了B锁
Thread-1释放了A锁
Thread-1抢到了B锁
Thread-2抢到了A锁
?(程序到这儿并没有结束,而是一直hang着...)
?
???‘‘‘
死锁代码示例

 递归锁

Rlock可以被第一个抢到锁的人连续的acquire和release
每acquire一次锁身上的计数加1
每release一次锁身上的计数减1
只要锁的计数不为0 其他人都不能抢
技术图片
from threading import Thread, Lock, current_thread, RLock
import time
?
# mutexA = Lock()
# mutexB = Lock()??
mutexA = mutexB = RLock()  # A B现在是同一把锁?
?
class MyThread(Thread):
?
    # 创建线程自动触发run方法 run方法内调用func1 func2相当于也是自动触发
    def run(self):  
        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(2):
    t = MyThread()
    t.start()??
?
‘‘‘执行结果:
Thread-1抢到了A锁
Thread-1抢到了B锁
Thread-1释放了B锁
Thread-1释放了A锁
Thread-1抢到了B锁
Thread-1抢到了A锁
Thread-1释放了A锁
Thread-1释放了B锁
Thread-2抢到了A锁
Thread-2抢到了B锁
Thread-2释放了B锁
Thread-2释放了A锁
Thread-2抢到了B锁
Thread-2抢到了A锁
Thread-2释放了A锁
Thread-2释放了B锁
??‘‘‘
递归锁代码示例

Event事件

前言
三两赛车准备出发,但是要等信号灯打出绿灯信号时,才出发

Event事件
事件处理的机制。
全局定义了一个内置标志Flag,
Flag默认值为 False,那么当程序执行 event.wait方法时就会阻塞,
如果Flag值为True,那么event.wait 方法时便不再阻塞。

涉及的方法:
set(): 将标志设为True,并通知所有处于等待阻塞状态的线程恢复运行状态。
clear(): 将标志设为False。
wait(timeout): 如果标志为True将立即返回,否则阻塞线程至等待阻塞状态,等待其他线程调用set()。
isSet(): 获取内置标志状态,返回True或False。
技术图片
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(3):
    t = Thread(target=car,args=(伞兵%s%i,))
    t.start()
?
‘‘‘执行结果:
红灯正亮着
伞兵0正在等红灯
伞兵1正在等红灯
伞兵2正在等红灯
(此处hang3s,因为sleep了3s)?
绿灯亮了
伞兵1加油门飙车了
伞兵0加油门飙车了
伞兵2加油门飙车了
?‘‘‘
重现 前言 的情景

 信号量

信号量semaphore
是一个变量,控制着对公共资源或者临界区的访问。
?信号量维护着一个计数器,指定可同时访问资源或者进入临界区的线程数。

实现机制:??
每次有一个线程获得信号量时,计数器-1。
?若计数器为0,其他线程就停止访问信号量,直到另一个线程释放信号量。
技术图片
# 信号量代码示例
from threading import Semaphore,Thread
import time
import random
?
sm = Semaphore(5)  # 造了一个含有五个的坑位的公共厕所
?
def task(name):
    sm.acquire()
    print(%s占了一个坑位%name)
    time.sleep(random.random())
    sm.release()
    print(%s释放了一个坑位 % name)
?
for i in range(4):
    t = Thread(target=task,args=(i,))
    t.start()
?
‘‘‘执行结果:
0占了一个坑位
1占了一个坑位
2占了一个坑位
3占了一个坑位
1释放了一个坑位
0释放了一个坑位
2释放了一个坑位
3释放了一个坑位
?‘‘‘??
信号量代码示例

补充讲解三种队列

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

 

GIL、Event事件、信号量、死锁、递归锁

标签:div   数据共享   priority   star   回收机制   rlock   研究   work   假设   

原文地址:https://www.cnblogs.com/buzaiyicheng/p/11352588.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!