目录
GIL全局解释锁
- GIL本质上是一个互斥锁。
- GIL是为了阻止同一个进程内多个进程同时执行(并行)
- 单个进程下的多个线程无法实现并行,但能实现并发
这把锁主要是因为Cpython的内存管理不是线程安全的
- 保证线程在执行任务时不会被垃圾回收机制回收
from threading import Thread
import time
num = 100
def task():
global num
num2 = num
time.sleep(1)
num = num2 - 1
print(num)
for line in range(100):
t = Thread(target=task)
t.start()
# 这里的运行结果都是99, 加了IO操作,所有线程都对num进行了减值操作,由于GIL锁的存在,没有修改成功,都是99
多线程的作用
- 计算密集型, 有四个任务,每个任务需要10s
单核:
- 开启进程
- 消耗资源过大
- 4个进程: 40s
- 开启线程
- 消耗资源远小于进程
- 4个线程: 40s
多核:
- 开启进程
- 并行执行, 效率比较高
- 4个进程: 10s
- 开启线程
- 并发执行,执行效率低
- 4个线程: 40s
- IO密集型, 四个任务, 每个任务需要10s
单核:
- 开启进程
- 消耗资源过大
- 4个进程: 40s
- 开启线程
- 消耗资源远小于进程
- 4个线程: 40s
多核:
- 开启进程
- 并行执行, 效率小于多线程, 但是遇到IO会立马切换CPU的执行权限
- 4个进程: 40s + 开启进程消耗的额外时间
- 开启线程
- 并发执行,执行效率高于多进程
- 4个线程: 40s
测试计算密集型
from threading import Thread
from multiprocessing import Process
import time
import os
# 计算密集型
def work1():
number = 0
for line in range(100000000):
number += 1
# IO密集型
def work2():
time.sleep(2)
if __name__ == '__main__':
# 测试计算密集型
print(os.cpu_count()) # 4核cpu
start = time.time()
list1 = []
for line in range(6):
p = Process(target=work1) # 程序执行时间8.756593704223633
# p = Thread(target=work1) # 程序执行时间31.78555393218994
list1.append(p)
p.start()
for p in list1:
p.join()
end = time.time()
print(f'程序执行时间{end - start}')
IO密集型
from threading import Thread
from multiprocessing import Process
import time
import os
# 计算密集型
def work1():
number = 0
for line in range(100000000):
number += 1
# IO密集型
def work2():
time.sleep(1)
if __name__ == '__main__':
# 测试计算密集型
print(os.cpu_count()) # 4核cpu
start = time.time()
list1 = []
for line in range(100):
# p = Process(target=work2) # 程序执行时间15.354223251342773
p = Thread(target=work2) # 程序执行时间1.0206732749938965
list1.append(p)
p.start()
for p in list1:
p.join()
end = time.time()
print(f'程序执行时间{end - start}')
结论:
- 在计算密集型的情况下, 使用多进程
- 在IO密集型的情况下, 使用多线程
- 高效执行多个进程, 内有多个IO密集型程序,使用多进程 + 多线程
死锁现象
指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,如无外力作用,它们都无法推进下去.此时称系统处于死锁状态
以下就是死锁:
from threading import Thread, Lock
from threading import current_thread
import time
mutex_a = Lock()
mutex_b = Lock()
class MyThread(Thread):
def run(self):
self.func1()
self.func2()
def func1(self):
mutex_a.acquire()
print(f'用户{self.name}抢到锁a')
mutex_b.acquire()
print(f'用户{self.name}抢到锁b')
mutex_b.release()
print(f'用户{self.name}释放锁b')
mutex_a.release()
print(f'用户{self.name}释放锁a')
def func2(self):
mutex_b.acquire()
print(f'用户{self.name}抢到锁b')
time.sleep(1)
mutex_a.acquire()
print(f'用户{self.name}抢到锁a')
mutex_a.release()
print(f'用户{self.name}释放锁a')
mutex_b.release()
print(f'用户{self.name}释放锁b')
for line 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
'''
# 一直等待
递归锁
用于解决死锁问题
RLock: 比喻成万能钥匙,可以提供给多个人使用
但是第一个使用的时候,会对该锁做一个引用计数
只有引用计数为0, 才能真正释放让一个人使用
上面的例子中用RLock代替Lock, 就不会发生死锁现象
from threading import Thread, Lock, RLock
from threading import current_thread
import time
# mutex_a = Lock()
# mutex_b = Lock()
mutex_a = mutex_b = RLock()
class MyThread(Thread):
def run(self):
self.func1()
self.func2()
def func1(self):
mutex_a.acquire()
print(f'用户{self.name}抢到锁a')
mutex_b.acquire()
print(f'用户{self.name}抢到锁b')
mutex_b.release()
print(f'用户{self.name}释放锁b')
mutex_a.release()
print(f'用户{self.name}释放锁a')
def func2(self):
mutex_b.acquire()
print(f'用户{self.name}抢到锁b')
time.sleep(1)
mutex_a.acquire()
print(f'用户{self.name}抢到锁a')
mutex_a.release()
print(f'用户{self.name}释放锁a')
mutex_b.release()
print(f'用户{self.name}释放锁b')
for line in range(10):
t = MyThread()
t.start()
信号量(了解)
互斥锁: 比喻成一个家用马桶, 同一时间只能让一个人去使用
信号比喻成公测多个马桶: 同一时间可以让多个人去使用
from threading import Semaphore
from threading import Thread
from threading import current_thread
import time
sm = Semaphore(5)
def task():
sm.acquire()
print(f'{current_thread().name}执行任务')
time.sleep(1)
sm.release()
for i in range(20):
t = Thread(target=task)
t.start()
线程队列
线程Q: 就是线程队列 FIFO
- 普通队列: 先进先出 FIFO
- 特殊队列: 后进先出 LIFO
- 优先级队列: 若传入一个元组,会依次判断参数的ASCII的数值大小
import queue
# 普通的线程队列: 遵循先进先出
q = queue.Queue()
q.put(1)
q.put(2)
q.put(3)
print(q.get()) # 1
print(q.get()) # 2
# LIFO队列 后进先出
q = queue.LifoQueue()
q.put(1)
q.put(2)
q.put(3)
print(q.get()) # 3
# 优先级队列:根据参数内
q = queue.PriorityQueue()
q.put((4, '我'))
q.put((2, '你'))
q.put((3, 'ta'))
print(q.get()) # (2, '你')