1.同步
例如8.6上一篇博客最后的那个问题,之所以会出现那样的情况原因就是,没有控制多个线程对同一资源的访问,对数据造成破坏,使得线程的结果不可预期,这样的现象称为‘线程不安全’
解决思路:利用同步来进行解决
from threading import Thread,Lock
import time
g=0
def hs1():
global g
l.acquire()
for i in range(100000):
g+=1
print(‘hs1’,g)
l.release()
def hs2():
global g
l.acquire()
for i in range(100000):
g+=1
print(‘hs2’,g)
l.release()
if name == ‘main’:
l=Lock()
h1=Thread(target=hs1)
h2=Thread(target=hs2)
h1.start()
h2.start()
线程执行函数1时,对其加锁,使得函数2的请求加锁的请求被阻碍,只有函数1完全执行完毕,释放锁后,线程2才能得到锁,继续执行下去。
2.异步
同步调用就是你喊朋友去吃饭,朋友在忙,你就一直等,等你朋友忙完,你们再一起去吃饭
异步调用就是你喊朋友去吃饭,朋友在忙说忙完找你,你就去做别的事了。
from multiprocessing import Pool
import time
def zuoye(name):
print(name,‘在写代码’)
time.sleep(1)
return name+‘写完代码了’
def chouyan(status):
print(‘去抽烟因为’+status)
if name == ‘main’:
p=Pool(1)
p.apply_async(zuoye,(‘张三’,),callback=chouyan)
p.close()
p.join()
3.互斥锁
当多个线程几乎同时修改一个共享数据时,需要进行同步控制,线程同步能够保证多个线程安全的访问竞争资源,最简单的机制就是使用互斥锁
当一个线程调用锁的acquire()方法获得锁时,锁就进入locked状态,每次只有一个线程可以得到锁,,如果此时另一个线程试图获得锁,那么该线程就会变为blocked状态,直到拥有锁的线程release()
好处:
确保了某段关键代码只能由一个线程从头到尾完整的执行
坏处:
阻止了多线程的并发执行,效率下降。由于可能存在多个锁,因此可能会造成死锁。
4.死锁
如果两个线程分别占有一部分资源,并同时等待对方发资源,就会造成死锁现象。
from threading import Thread,Lock
import time
g=0
def hs1():
global g
for i in range(100000):
l1.acquire()
print(‘函数1锁1得到锁,请求锁2’)
time.sleep(1)
l2.acquire()
print(‘1111111111111111111111’)
g+=1
l2.release()
l1.release()
print(‘hs1’,g)
def hs2():
global g
for i in range(100000):
l2.acquire()
print(‘函数2锁2得到锁,请求锁1’)
time.sleep(1)
l1.acquire()
print(‘22222222222222222222222’)
l1.release()
l2.release()
g+=1
print(‘hs2’,g)
if name == ‘main’:
l1=Lock()
l2=Lock()
h1=Thread(target=hs1)
h2=Thread(target=hs2)
h1.start()
h2.start()
5.队列
import queue
q=queue.Queue(3)
q.put(1)
q.put(2)
q.put(3)
# print(‘1111111’,q.full())
# q.put(4)
while q.qsize()>0:
print(q.get())
print(‘end’)
队列优先级
import queue
q=queue.PriorityQueue(5)
q.put((1,‘a’))
q.put((5,‘xj’))
q.put((2,‘xg’))
q.put((-9,‘mg’))
while q.qsize()>0:
print(q.get())
队列后入先出
import queue
q=queue.LifoQueue(4)
q.put(1)
q.put(2)
q.put(3)
q.put(4)
while q.qsize()>0:
print(q.get())
6.threadlocal
在多线程环境下,每个线程都有自己的数据,一个线程使用自己的局部变量比使用全局变量要好,因为局部变量只有自己能看见,不会影响其他线程,而全局变量的修改必须加锁
from threading import Thread
import time,threading
local=threading.local()
def hanshu1(name):
local.name=name
# for i in range(100000):
# pass
hanshu2()
def hanshu2():
print(threading.current_thread(),local.name)
if name == ‘main’:
t1=Thread(target=hanshu1,args=(‘张三’,),name=‘线程帅哥’)
t2=Thread(target=hanshu1,args=(‘李四’,),name=‘线程美女’)
t1.start()
t2.start()
7.协程
协程是比线程更小的执行单元,协程自带上下文,只要在合适的时机,我们可以吧一个协程切换到另一个协程。通俗的理解:在一个线程中某个函数,可以在任何地方保存当前函数的一些临时变量等信息,然后在切换到另一个函数中执行,注意,这不是通过函数调用做到的,切换的次数和时机都是由开发者决定的。
这个过程看起来和线程差不多,但其实不然,线程切换从系统层面远不止保存和恢复cpu上下文那么简单,线程切换非常耗性能,但协程的切换只是单纯操作cpu上下文。