今日内容
一、线程的扩展
1.1信号量:
什么是信号量:互斥锁是同一时间内只有拿到锁的才能运行,信号量是设置多个锁,最多能抢到设置锁的最大值
例:
from threading import Thread,Semaphore,current_thread
import time,random
def func():
sm.acquire()
print('%s get sm' %current_thread().getName())
time.sleep(random.randint(1,5))
sm.release()
if __name__ == '__main__':
sm=Semaphore(5) #设置有几把锁
for i in range(23):
t=Thread(target=func)
t.start()
1.2死锁
死锁:在同一进程中的多个线程互相抢到锁没释放又在抢对方的锁,导致都在等对方释放锁卡在原地
例:
from threading import Thread,Lock
import time
mutexA = Lock()
mutexB = Lock()
class Mythread(Thread):
def __init__(self,name):
super().__init__()
self.name = name
def run(self):
self.f1()
self.f2()
def f1(self):
mutexA.acquire()
print("%s 抢到了A锁" %self.name)
mutexB.acquire()
print("%s 抢到了B锁" %self.name)
mutexB.release()
mutexA.release()
def f2(self):
mutexB.acquire()
print("%s 抢到了B锁" %self.name)
time.sleep(0.1)
mutexA.acquire()
print("%s 抢到了A锁" %self.name)
mutexA.release()
mutexB.release()
if __name__ == '__main__':
t1 = Mythread("线程1")
t2 = Mythread("线程2")
t1.start()
t2.start()
1.3递归锁
#什么是递归锁:首先拿到不管是A锁还是B锁的线程锁的数量会累加1,当锁的数量不是0的时候其他的线程不可以抢锁,
#不管释放A锁还是B锁的时候会将锁的数量减1,当锁的数量是0的时候其他进程才可以开始抢锁
例:
from threading import Thread,RLock,Lock
import time
mutexA = mutexB = RLock()
class Mythread(Thread):
def __init__(self,name):
super().__init__()
self.name = name
def run(self) -> None:
self.f1()
self.f2()
def f1(self):
mutexA.acquire()
print("%s 抢到了A锁" %self.name)
mutexB.acquire()
print("%s 抢到了B锁" %self.name)
mutexB.release()
mutexA.release()
def f2(self):
mutexB.acquire()
print("%s 抢到了B锁" % self.name)
time.sleep(0.1)
mutexA.acquire()
print("%s 抢到了A锁" % self.name)
mutexA.release()
mutexB.release()
if __name__ == '__main__':
t1 = Mythread("线程1")
t2 = Mythread("线程2")
t3 = Mythread("线程3")
t4 = Mythread("线程4")
t1.start()
t2.start()
t3.start()
t4.start()
print("主线程")
1.4事件event与内置方法
#什么是事件:因为一个进程内的多个线程共享这个进程的资源,而且是各自运行的,所以控制一个线程结束之后再运行另一个线程可以设置一个全局变量
例:
from threading import Event,Thread,current_thread
import time
e = Event() #默认全局变量非False
def f1():
print("%s 运行" % current_thread().name)
time.sleep(3)
e.set() #将全局变量改为True
# e.clear() #将全局变量改为False
# e.is_set() #判断有没有改全局变量为True
def f2():
e.wait() #等到全局变量为Ture
print("%s 运行" % current_thread().name )
if __name__ == '__main__':
t1 = Thread(target=f1)
t2 = Thread(target=f2)
t1.start()
t2.start()
#红绿灯举例:
from threading import Event,Thread,current_thread
import time,random
e = Event()
def task1():
while True:
e.clear()
print("红灯亮")
time.sleep(2)
e.set()
print("绿灯亮")
time.sleep(3)
def task2():
while True:
if e.is_set():
print(" %s溜了溜了" %current_thread().name)
break
else:
print("%s等等等等等" % current_thread().name)
e.wait()
if __name__ == '__main__':
Thread(target=task1).start()
while True:
time.sleep(random.randint(1,3))
Thread(target=task2).start()
1.5定时器
from threading import Timer
def hello():
print("hello, world")
#定时秒数,执行的函数
t = Timer(5, hello)
t.start()
二、线程queue
2.1队列:先进先出,先存进去先取出来
import queue
q=queue.Queue()
q.put('first')
q.put('second')
q.put('third')
print(q.get())
print(q.get())
print(q.get())
2.2堆栈:后进先出
import queue
q=queue.LifoQueue()
q.put('first')
q.put('second')
q.put('third')
print(q.get())
print(q.get())
print(q.get())
2.3优先级队列:设置优先级,优先级低的先取出来
import queue
q=queue.PriorityQueue()
#put进入一个元组,元组的第一个元素是优先级(通常是数字,也可以是非数字之间的比较),数字越小优先级越高
q.put((20,'a'))
q.put((10,'b'))
q.put((30,'c'))
print(q.get())
print(q.get())
print(q.get())
三、GIL(Cpython解释器锁)
3.1什么是GIL:
锁的目的是为了保护共享的数据,同一时间只能有一个线程来修改共享的数据,而保护不同的数据应该用不同的锁
首先LOCK锁的是文件,为了保证文件数据的安全,不能让所有线程都同时进行修改,所以将文件锁了的话,只有抢到锁的线程才能进行对文件进行操作
而GIL锁的是Cpython解释器,为了保护解释器级别的数据(比如垃圾回收的数据),不能让所有线程都同时进行修改,
所以这时抢到GIL锁的线程才会将CPU资源分配给他,所以负责垃圾回收线程不会和修改文件的线程同时进行
例: 1.100个线程去抢GIL锁,即抢执行权限
2. 肯定有一个线程先抢到GIL(暂且称为线程1),然后开始执行,一旦执行就会拿到lock.acquire()
3. 极有可能线程1还未运行完毕,CPU就因为正在进行IO或者占用时间太长进行切换,会将GIL锁释放,就有另外一个线程2抢到GIL,然后开始运行,但线程2发现互斥锁lock还未被线程1释放,于是阻塞,被迫交出执行权限,即释放GIL
4.直到线程1重新抢到GIL,开始从上次暂停的位置继续执行,直到正常释放互斥锁lock,然后其他的线程再重复2 3 4的过程