同步异步
同步:协调同步,按预定的先后次序进行运行。
异步:
互斥锁
线程同步能够保证多个线程安全的访问竞争资源(全局内容),最简单的同步机制就是使用互斥锁。
threading模块中定义了Lock类
from threading import Thread,Lock#创建锁
g=0
def hanshu1():
global g
for i in range(100000):
l.acquire()#锁定,默认为True
g+=1
l.release()#释放锁
print("函数1",g)
def hanshu2():
global g
for i in range(100000):
l.acquire()
g += 1
l.release()
print("函数2",g)
if __name__ == '__main__':
l=Lock()
t1=Thread(target=hanshu1)
t2=Thread(target=hanshu2)
t1.start()
t2.start()
#第一个结束的肯定是大于100000
#第二个结束的时候才是200000
#思考模式:扔钢镚
'''
函数1 177131
函数2 200000
'''
from threading import Thread,Lock
g=0
def hanshu1():
global g
l.acquire()
for i in range(100000):
g+=1
print("函数1",g)
l.release()
def hanshu2():
global g
l.acquire()
for i in range(100000):
g += 1
print("函数2",g)
l.release()
if __name__ == '__main__':
l=Lock()
t1=Thread(target=hanshu1)
t2=Thread(target=hanshu2)
t1.start()
t2.start()
#这两个就会是第一个100000
#一个200000,因为第一个加完,第二个才回去加
'''
函数1 100000
函数2 200000
'''
总结:
锁的好处:
1.确保了某段关键代码只能由一个线程从头到尾完整地执行。
锁的坏处:
2.阻止了多线程并发执行,包含锁的某段代码实际上只能以单线程模式执行,效率就大大地下降了。
3.由于可以存在多个锁,不同的线程持有不同的锁,并试图获取对方持有的锁时,可能会造成死锁。
死锁
from threading import Thread,Lock#创建锁
import time
g=0
def hanshu1():
global g
for i in range(100000):
lock1.acquire()#锁定lock1锁定,lock2也锁定,lock1等lock2释放,lock2等lock1释放
print('函数1锁1得到锁,请求锁2')
time.sleep(1)
lock2.acquire()
print('111111111111111')
g+=1
lock2.release()
lock1.release()#释放锁
print("函数1",g)
def hanshu2():
global g
for i in range(100000):
lock2.acquire() # 锁定
print('函数2锁2得到锁,请求锁1')
time.sleep(1)
lock1.acquire()
print('2222222222222')
g += 1
lock1.release()
lock2.release() # 释放锁
print("函数2",g)
if __name__ == '__main__':
lock1=Lock()
lock2 = Lock()
t1=Thread(target=hanshu1)
t2=Thread(target=hanshu2)
t1.start()
t2.start()
'''
函数1锁1得到锁,请求锁2
函数2锁2得到锁,请求锁1
'''
生产者和消费者
队列
常用操作
import queue
q=queue.Queue(3)
q.put(1)
q.put(2)
q.put(3)
print('11111111',q.full())
# q.put(4)
while q.qsize()>0:
print(q.get())
# while q.qsize():
# print(q.get())
print("结束")
for i in range(q.qsize()):
print(q.get(),i)
队列优先级
#队列优先级:数字越小,优先级越高
import queue
q=queue.PriorityQueue(5)
q.put((1,'香蕉'))
q.put((5,'葡萄'))
q.put((2,'西瓜'))
q.put((-9,'芒果'))
while q.qsize()>0:
print(q.get())
'''
(-9, '芒果')
(1, '香蕉')
(2, '西瓜')
(5, '葡萄')
'''
后入先出队列
import queue
q=queue.LifoQueue(4)#后入先出
q.put(1)
q.put(2)
q.put(5)
q.put(4)
while q.qsize()>0:
print(q.get())
'''
4
5
2
1
'''
生产者与消费者模式
#生产者与消费者
from threading import Thread
import queue,time
def produce(q,name):
count=1
while True:
bz=name+"生产的包子%d"%count
q.put(bz)
print(bz)
time.sleep(1)
count+=1
def consum(q,name):
while True:
bz=q.get()
print(name,'吃',bz)
if __name__ == '__main__':
q=queue.Queue(5)
t1=Thread(target=produce,args=(q,"张三"))
t2 = Thread(target=consum, args=(q, "李四"))
t1.start()
t2.start()
#生产者与消费者:两个人包包子,三个人吃
from threading import Thread
import queue,time
def produce(q,name):
count=1
while True:
bz=name+"生产的包子%d"%count
q.put(bz)
print(bz)
time.sleep(1)
count+=1
def consum(q,name):
while True:
bz=q.get()
print(name,'吃',bz)
if __name__ == '__main__':
q=queue.Queue(5)
p1=Thread(target=produce,args=(q,"张三"))
p2 = Thread(target=produce, args=(q, "李四"))
c1 = Thread(target=consum, args=(q, "小孩1"))
c2 = Thread(target=consum, args=(q, "小孩2"))
c3 = Thread(target=consum, args=(q, "小孩3"))
p1.start()
p2.start()
c1.start()
c2.start()
c3.start()
全局ThreadLocal对象
每个线程都只读自己线程的独立副本,互不干扰。
#当不是全局ThreadLocal对象时:
from threading import Thread
import time,threading
class A():
pass
def hanshu1(name):
A.name=name
for i in range(1,1000000):
pass
hanshu2()
def hanshu2():
# time.sleep(1)
print(threading.current_thread(),A.name)
if __name__ == '__main__':
t1=Thread(target=hanshu1,args=("张三",),name="线程帅哥")#name="线程帅哥"这是线程的名字
t2 = Thread(target=hanshu1, args=("李四",), name="线程美女")
t1.start()
t2.start()
'''
<Thread(线程帅哥, started 4124)> 李四
<Thread(线程美女, started 12092)> 李四
'''
#改成ThreadLocal对象:
from threading import Thread
import time,threading
local=threading.local()
def hanshu1(name):
local.name=name
for i in range(1,1000):
pass
hanshu2()
def hanshu2():
# time.sleep(1)
print(threading.current_thread(),local.name)
if __name__ == '__main__':
t1=Thread(target=hanshu1,args=("张三",),name="线程帅哥")#name="线程帅哥"这是线程的名字
t2 = Thread(target=hanshu1, args=("李四",), name="线程美女")
t1.start()
t2.start()
'''
<Thread(线程帅哥, started 11892)> 张三
<Thread(线程美女, started 9424)> 李四
'''
全局解释器锁
对python虚拟机的访问是由全局解释器(GIL)控制的。这个锁是用来保证同时只能有一个线程运行的。
两个时间差不多。说明一次只能执行一个线程。
import time,threading
def hanshu2():
s=time.time()
count=0
for i in range(100000000):
count+=1
e=time.time()
print(e-s)
if __name__ == '__main__':
t1=threading.Thread(target=hanshu2)
t2 = threading.Thread(target=hanshu2)
t1.start()
t2.start()
'''
13.303761005401611
13.367764472961426
'''
import time, threading
def hanshu1():
s = time.time()
count = 0
for i in range(200000000):
count += 1
e = time.time()
print(e - s)
if __name__ == '__main__':
t1 = threading.Thread(target=hanshu1)
t1.start()
'''
13.543774604797363
'''
协程
协程又称微线程。
协程是比线程更小的执行单元。为什么说他是一个执行单元,因为它自带Cpu上下文。
通俗的理解:在一个线程中的某个函数,都可以任何地方保存当前函数的所有变量等信息,来回切换由开发者决定。
生成器的状态也是可以保留的。
def hanshu():
a=3
for x in range(10):
yield x+a
a+=1
x=hanshu()
for y in x:
print(y)
'''
状态被保留
0+3
1+4
2+5
'''
def hanshu():
for i in range(3):
print("函数1",i)
yield
def hanshu2(a):
for i in range(3):
next(a)
print("函数2", i)
h=hanshu()
hanshu2(h)
'''
函数1 0
函数2 0
函数1 1
函数2 1
函数1 2
函数2 2
'''
def hanshu():
for i in range(3):
print("函数1",i)
yield
def hanshu2(a):
for i in range(3):
next(a)
print("函数2", i)
h=hanshu()
hanshu2(h)
'''
函数1 0
函数2 0
函数1 1
函数2 1
函数1 2
函数2 2
'''