1.GIL全局解释器锁(******)
GIL:global interpreter lock,是一个互斥锁,存在于Cpython解释器内:
保证数据的安全(以牺牲效率来换取数据的安全)
阻止同一个进程内多个线程同时执行(不能并行但是能够实现并发)
线程是执行单位,但是不能直接运行,需要先拿到python解释器解释之后才能被cpu执行
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.)PIC定义
GIL全局解释器存在的原因:Cpython解释器的内存管理不是线程安全的
Cpython解释器的内存管理:垃圾回收机制
1)引用计数
2)标记清除
3)分代回收
垃圾回收机制也是一个任务,和普通的代码不是串行运行,如果是串行会明显有卡顿垃圾回收到底是开进程还是开线程?肯定是线程,所以想运行也必须要拿到python解释器假设能够并行,会出现什么情况?一个线程刚好要造一个a=1的绑定关系之前,这个垃圾线程来扫描,矛盾点就来了,谁成功都不对!也就意味着在Cpython解释器上有一把GIL全局解释器锁
所以在Cpython解释器中,同一个进程下:
多个线程不能实现并行但是能够实现并发
多个进程下的线程能够实现并行
问题:python多线程是不是就没有用了呢?
1)四个任务:计算密集的任务,每个任务耗时10s
单核情况下:
多线程好一点,消耗的资源少一点
多核情况下:
开四个进程:10s多一点
开四个线程:40s多一点
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) # 耗时9.252728939056396 p = Thread(target=work) # 耗时35.369622230529785 l.append(p) p.start() for p in l: p.join() stop = time.time() print('run time is %s' % (stop - start))计算密集型
2)四个任务:IO密集的任务,每个任务io 10s
单核情况下:
多线程好一点
多核情况下:
多线程好一点
from multiprocessing import Process from threading import Thread import os, time def work(): time.sleep(2) if __name__ == '__main__': l = [] print(os.cpu_count()) # 本机为8核 start = time.time() for i in range(600): p = Process(target=work) # 耗时4.699530839920044 # p=Thread(target=work) # 耗时2.054128885269165 l.append(p) p.start() for p in l: p.join() stop = time.time() print('run time is %s' % (stop - start))IO密集型
>>> 多线程和多进程都有自己的优点,要根据项目需求合理选择
2.GIL全局解释器锁与普通的互斥锁的区别
1)对于不同的数据,要想保证安全,需要加不同的锁处理
2)GIL并不能保证数据的安全,它是对Cpython解释器加锁,针对的是线程
3)保证的是同一个进程下多个线程之间的安全
from threading import Thread, Lock import time mutex = Lock() n = 100 def task(): global n mutex.acquire() tmp = n time.sleep(0.1) n = tmp - 1 mutex.release() t_list = [] for i in range(100): t = Thread(target=task) t.start() t_list.append(t) for t in t_list: t.join() print(n)
3.死锁与递归锁(了解)
自定义锁一次acquire必须对应一次release,不能连续acquire
递归锁可以连续的acquire,每acquire一次计数加一:针对的是第一个抢到锁的人
from threading import Thread,Lock,RLock import time # mutexA = Lock() # mutexB = Lock() # 形成死锁 mutexA = mutexB = RLock() # 抢锁一次计数加一,针对的是第一个抢到锁的人 class MyThead(Thread): def run(self): self.func1() self.func2() def func1(self): mutexA.acquire() print('%s 抢到A锁了' % self.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(100): t = MyThead() t.start()
4.信号量(了解)
普通互斥锁:普通的互斥锁是独立卫生间,所有人抢一把锁
信号量:公共卫生间,有多个坑,所有人抢多把锁
from threading import Thread, Semaphore import time import random sm = Semaphore(5) # 五个厕所五把锁 def task(name): sm.acquire() print('%s正在蹲坑' % name) # 模拟蹲坑耗时 time.sleep(random.randint(1, 5)) sm.release() if __name__ == '__main__': for i in range(20): t = Thread(target=task, args=('伞兵%s号' % i,)) t.start()
5.event时间
一些线程需要等待另外一些线程运行完毕才能运行,类似于发射信号一样
from threading import Event, Thread import time event = Event() def light(): print('红灯亮着!') time.sleep(3) event.set() # 解除阻塞,给我的event发了一个信号 print('绿灯亮了!') def car(i): print('%s 正在等红灯了' % i) event.wait() # 阻塞 print('%s 加油门飙车了' % i) t1 = Thread(target=light) t1.start() for i in range(10): t = Thread(target=car, args=(i,)) t.start()
6.线程queue
同一个进程下的线程数据都是共享的为什么还要用queue?
queue本身自带锁的功能,能够保证数据的安全
1)Queue 普通:先进先出
2)LifoQueue 先进后出:last in first out → 堆栈
3)PriorityQueue 优先级:用数字表示,数字越小优先级越高
import queue q=queue.Queue(3) q.put(1) q.put(2) q.put(3) print(q.get()) print(q.get()) print(q.get()) q = queue.LifoQueue(5) q.put(1) q.put(2) q.put(3) q.put(4) print(q.get()) q = queue.PriorityQueue() q.put((10,'a')) q.put((-1,'b')) q.put((100,'c')) print(q.get()) print(q.get()) print(q.get()) >>>: 1 2 3 4 (-1, 'b') (10, 'a') (100, 'c')
7.进程池线程池(******)
8.如何使用进程池线程池(******)