一、线程锁
from threading import Thread,Lock
l = Lock() #实例化一个锁对象
x = 0
def task():
global x
l.acquire() # 拿钥匙
for i in range(100000): #252990 出现了运算错误 由于三个线程抢占cpu导致的,所以我们需要给他加锁
x +=1
l.release() #还钥匙
if __name__ == '__main__':
t1 = Thread(target=task)
t2 = Thread(target=task)
t3 = Thread(target=task)
l_t = [t1,t2,t3]
for t in l_t:
t.start()
t.join()
print(x)
'''
300000 这样结果就再也没有变过
'''
二、死锁和递归锁
死锁:
两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程
举个栗子:三个人吃面
from threading import Thread,Lock
import time
chopsticks_lock = Lock() #实例化一个筷子锁
noodles_lock = Lock() #实例化一个面条锁
def eat1(name): #开启第一个线程
chopsticks_lock.acquire() #给筷子加锁
print(f"{name }--抢到了筷子")
noodles_lock.acquire() #给面条加锁
print(f"{name }--抢到了面条")
print(f"{name }--在吃面")
chopsticks_lock.release() #还钥匙
noodles_lock.release() #还钥匙
def eat2(name): #开启第二个线程
noodles_lock.acquire() # 给面条加锁
print(f"{name}--抢到了面条")
time.sleep(1)
chopsticks_lock.acquire() # 给筷子加锁
print(f"{name }--抢到了筷子")
print(f"{name }--在吃面")
chopsticks_lock.release() # 还钥匙
noodles_lock.release() # 还钥匙
if __name__ == '__main__':
l_name = ['线程1','线程2','线程3'] #定义一个吃面的人的列表
for name in l_name:
t1 = Thread(target=eat1,args=(name,)) #实例化第一个线程
t2 = Thread(target=eat2,args=(name,)) #实例化第二个线程
t1.start()
t2.start()
'''
线程1--抢到了筷子
线程1--抢到了面条
线程1--在吃面
线程1--抢到了面条
线程2--抢到了筷子
'''
#这样就造成了死锁问题,杨蓬蓬--抢到了面条,王文彬--抢到了筷子,导致两个人都吃不了面,只有同时拿到面条课筷子才能吃面
'''
线程2拿到了(锁头1-筷子)想要往下执行需要(锁头2-面条)
线程1拿到了(锁头2-面条)想要往下执行需要(锁头1-筷子),
互相都拿到了彼此想要往下执行的必需条件,互相都不放手里的锁头.
'''
用递归锁解决死锁问题:
递归锁:
在同一个线程内可以被多次acquire
就上面三个人吃面的例子,我们所说的死锁也就是带有筷子锁和面条所得一串锁,只要一串锁拿走了,其他人就拿不到锁。
from threading import Thread,Lock,RLock
import time
chopsticks_lock = noodles_lock = RLock() #实例化一个递归锁
def eat1(name): #开启第一个线程
chopsticks_lock.acquire() #给筷子加锁
print(f"{name }--抢到了筷子")
time.sleep(1)
noodles_lock.acquire() #给面条加锁
print(f"{name }--抢到了面条")
print(f"{name }--在吃面")
chopsticks_lock.release() #还钥匙
noodles_lock.release() #还钥匙
def eat2(name): #开启第二个线程
noodles_lock.acquire() # 给面条加锁
print(f"{name}--抢到了面条")
time.sleep(1)
chopsticks_lock.acquire() # 给筷子加锁
print(f"{name }--抢到了筷子")
print(f"{name }--在吃面")
chopsticks_lock.release() # 还钥匙
noodles_lock.release() # 还钥匙
if __name__ == '__main__':
l_name = ['线程1','线程2','线程3'] #定义一个吃面的人的列表
for name in l_name:
t1 = Thread(target=eat1,args=(name,)) #实例化第一个线程
t2 = Thread(target=eat2,args=(name,)) #实例化第二个线程
t1.start()
t2.start()
'''
线程1--抢到了筷子
线程1--抢到了面条
线程1--在吃面
线程1--抢到了面条
线程1--抢到了筷子
线程1--在吃面
线程2--抢到了筷子
线程2--抢到了面条
线程2--在吃面
线程2--抢到了面条
线程2--抢到了筷子
线程2--在吃面
线程3--抢到了筷子
线程3--抢到了面条
线程3--在吃面
线程3--抢到了面条
线程3--抢到了筷子
线程3--在吃面
'''
1|3三、信号量(Semaphore)
规定几个线程一同运行,就必须几个线程一同运行,不能多,例如KTV只能一次进4个人,只有有人走了,其他人才能进去。那我们就来举一个KTV的例子
from threading import Thread,Semaphore
import time
def KTV(name):
sm.acquire()
print(f"{name}走进了KTV")
time.sleep(2)
print(f"{name}走出了KTV")
time.sleep(4)
sm.release()
if __name__ == '__main__':
sm = Semaphore(4) #规定一次只能有4个人进入KTV
for i in range(15):
t = Thread(target=KTV,args=(i+1,))
t.start()
'''
1走进了KTV
2走进了KTV
3走进了KTV
4走进了KTV
1走出了KTV
4走出了KTV
2走出了KTV
3走出了KTV
......
5走进了KTV
7走进了KTV
6走进了KTV
8走进了KTV
'''
四、GIL
在Cpython解释器中有一把GIL锁(全局解释器锁),GIl锁本质是一把互斥锁。导致了同一个进程下,同一时间只能运行一个线程,无法利用多核优势.同一个进程下多个线程只能实现并发不能实现并行.
为什么要有GIL?
因为cpython自带的垃圾回收机制不是线程安全的,所以要有GIL锁.
计算密集型 :推荐使用多进程
每个都要计算10s
多线程
在同一时刻只有一个线程会被执行,也就意味着每个10s都不能省,分开每个都要计算10s,共40.ns
多进程
可以并行的执行多个线程,10s+开启进程的时间
IO密集型: 推荐使用多线程
4个任务每个任务90%大部分时间都在io.
多线程
可以实现并发,每个线程io的时间不咋占用cpu, 10s + 4个任务的计算时间
多进程
可以实现并行,10s+1个任务执行的时间+开进程的时间
下面我们就来看这样一个例子:
'''计算密集型'''
#推荐使用多进程
from threading import Thread
from multiprocessing import Process
import time
def task():
x = 0
for i in range(10000000):
x *= i
if __name__ == '__main__':
list = []
start = time.time()
for i in range(4):
# p = Process(target=task) #实例化一个子进程
# list.append(p)
# p.start()
t = Thread(target=task)
list.append(t)
t.start()
for m in list:
m.join()
end= time.time()
# print('多进程',end-start) #多进程 2.8643341064453125
print('多线程', end-start) #多线程 3.020913600921631
'''看这个时间你就会一目了然'''
from threading import Thread
from multiprocessing import Process
import time
def task():
time.sleep(2)
print('哈哈哈!好好学习天天向上!!!')
if __name__ == '__main__':
list = []
start = time.time()
for i in range(4):
p = Process(target=task) #实例化一个子进程
list.append(p)
p.start()
# t = Thread(target=task)
# list.append(t)
# t.start()
for m in list:
m.join()
end= time.time()
print('多进程',end-start) #多进程 3.662198781967163
# print('多线程', end-start) #多线程 2.0046467781066895
'''看这个时间你就会一目了然'''