2019.09.18学习整理
线程锁
同步锁(线程的互斥锁)
多线程修改数据会造成混乱
from threading import Thread,current_thread,Lock
import time
x = 0
def task():
global x
for i in range(100000): # 最少10万级别才能看出来
x = x+1 # 有可能右边的x刚拿到了0,
# 发生线程不安全的原因:
# t1 x+1 阶段 x = 0 保存了状态 cpu被切走 t2 x+1 x = 0 保存了状态 cpu被切走
# 下次t1 继续运行代码 x = 0+1 下次t2 再运行代码的时候也是 x = 0+1
# 也就说修改了两次 x 只加了一次1 。
# time.sleep()
# lock.release()
if __name__ == '__main__':
t_list = []
for i in range(3):
t = Thread(target=task)
t_list.append(t)
t.start()
for i in t_list:
i.join()
print(x) # 99
使用线程锁解决线程修改数据混乱问题
from threading import Thread,current_thread,Lock
import time
x = 0
lock = Lock()
def task():
global x
lock.acquire()
for i in range(100000): # 最少10万级别才能看出来
x = x+1 # 有可能右边的x刚拿到了0,
lock.release()
if __name__ == '__main__':
t_list = []
for i in range(3):
t = Thread(target=task)
t_list.append(t)
t.start()
for i in t_list:
i.join()
print(x) # 99
死锁问题
from threading import Thread,Lock
import time
mutex1=Lock()
mutex2=Lock()
class MyT(Thread):
def run(self):
self.task1()
self.task2()
def task1(self):
mutex1.acquire()
print(f'{self.name}抢到了锁1')
mutex2.acquire()
print(f'{self.name}抢到了锁2')
mutex2.release()
print(f'{self.name}释放了锁2')
mutex1.release()
print(f'{self.name}释放了锁1')
def task2(self):
mutex2.acquire()
print(f'{self.name}抢到了锁2')
time.sleep(1)
mutex1.acquire()
print(f'{self.name}抢到了锁1')
mutex1.release()
print(f'{self.name}释放了锁1')
mutex2.release()
print(f'{self.name}释放了锁2')
for i in range(3):
t=MyT()
t.start()
# 两个线程
# 线程1拿到了(锁头2)想要往下执行需要(锁头1),
# 线程2拿到了(锁头1)想要往下执行需要(锁头2)
# 互相都拿到了彼此想要往下执行的必需条件,互相都不放手里的锁头.
解决方法,递归锁
在Python中为了支持在同一线程中多次请求同一资源,python提供了可重入锁RLock。
这个RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源。上面的例子如果使用RLock代替Lock,则不会发生死锁:
递归锁
from threading import Thread,Lock,RLock
# 递归锁 在同一个线程内可以被多次acquire
# 如何释放 内部相当于维护了一个计数器 也就是说同一个线程 acquire了几次就要release几次
# mutex1 = Lock()
# mutex2 = Lock()
mutex1 = RLock()
mutex2 = mutex1
import time
class MyThreada(Thread):
def run(self):
self.task1()
self.task2()
def task1(self):
mutex1.acquire()
print(f'{self.name} 抢到了 锁1 ')
mutex2.acquire()
print(f'{self.name} 抢到了 锁2 ')
mutex2.release()
print(f'{self.name} 释放了 锁2 ')
mutex1.release()
print(f'{self.name} 释放了 锁1 ')
def task2(self):
mutex2.acquire()
print(f'{self.name} 抢到了 锁2 ')
time.sleep(1)
mutex1.acquire()
print(f'{self.name} 抢到了 锁1 ')
mutex1.release()
print(f'{self.name} 释放了 锁1 ')
mutex2.release()
print(f'{self.name} 释放了 锁2 ')
for i in range(3):
t = MyThreada()
t.start()
信号量Semaphore
同进程的一样
Semaphore管理一个内置的计数器,
每当调用acquire()时内置计数器-1;
调用release() 时内置计数器+1;
计数器不能小于0;当计数器为0时,acquire()将阻塞线程直到其他线程调用release()。
from threading import Thread,Semaphore
import threading
import time
def task():
sm.acquire()
print(f"{threading.current_thread().name} get sm")
time.sleep(3)
sm.release()
if __name__ == '__main__':
sm = Semaphore(5) # 同一时间只有5个进程可以执行。
for i in range(20):
t = Thread(target=task)
t.start()
GIL
在Cpython解释器中有一把GIL锁(全局解释器锁),GIl锁本质是一把互斥锁。
导致了同一个进程下,同一时间只能运行一个线程,无法利用多核优势.
同一个进程下多个线程只能实现并发不能实现并行.
为什么要有GIL?
因为cpython自带的垃圾回收机制不是线程安全的,所以要有GIL锁.
导致了同一个进程下,同一时间只能运行一个线程,无法利用多核优势.
分析:
我们有四个任务需要处理,处理方式肯定是要玩出并发的效果,解决方案可以是:
方案一:开启四个进程
方案二:一个进程下,开启四个线程
计算密集型 推荐使用多进程
每个都要计算10s
多线程
在同一时刻只有一个线程会被执行,也就意味着每个10s都不能省,分开每个都要计算10s,共40.ns
多进程
可以并行的执行多个线程,10s+开启进程的时间
io密集型 推荐多线程
4个任务每个任务90%大部分时间都在io.
每个任务io10s 0.5s
多线程
可以实现并发,每个线程io的时间不咋占用cpu, 10s + 4个任务的计算时间
多进程
可以实现并行,10s+1个任务执行的时间+开进程的时间
多进程vs多线程
from threading import Thread
from multiprocessing import Process
import time
# 计算密集型
# def work1():
# res=0
# for i in range(100000000): #1+8个0
# res*=i
#
# if __name__ == '__main__':
# t_list = []
# start = time.time()
# for i in range(4):
# # t = Thread(target=work1)
# t = Process(target=work1)
# t_list.append(t)
# t.start()
# for t in t_list:
# t.join()
# end = time.time()
# # print('多线程',end-start) # 多线程 15.413789510726929
# print('多进程',end-start) # 多进程 4.711405515670776
# # io密集型
# def work1():
# x = 1+1
# time.sleep(5)
#
#
# if __name__ == '__main__':
# t_list = []
# start = time.time()
# for i in range(4):
# t = Thread(target=work1)
# # t = Process(target=work1)
# t_list.append(t)
# t.start()
# for t in t_list:
# t.join()
# end = time.time()
# print('多线程',end-start) # 多线程 5.002625942230225
# # print('多进程',end-start) # 多进程 5.660863399505615