python基础之多线程锁机制

GIL(全局解释器锁)python

GIL并非Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念,是为了实现不一样线程对共享资源访问的互斥,才引入了GIL多线程

在Cpython解释器中,同一个进程下开启的多线程,同一时刻只能有一个线程执行,没法利用多核优点并发

python对于计算密集型的任务开多线程的效率甚至不如串行(没有大量切换),可是,对于IO密集型的任务效率仍是有显著提高的。app

GIL原理图ui

 

计算密集型:结果确定是100,由于每一次start结果就已经出来了,因此第二个线程确定是经过调用第一个线程的count值进行计算的线程

 1 def sub():
 2     global count
 3 
 4     '''线程的公共数据  下'''
 5     temp=count
 6     count=temp+1
 7     '''线程的公共数据  上'''
 8 
 9     time.sleep(2)
10 count=0
11 
12 l=[]
13 for i in range(100):
14     t=threading.Thread(target=sub,args=())
15     t.start()  #每一次线程激活,申请一次gillock
16     l.append(t)
17 for t in l:
18     t.join()
19 print(count)

 

io密集型:当第一个线程开始start的时候,因为sleep了0.001秒,这0.001秒对于人而言很短,可是对于cpu而言,这0.001秒已经作了不少的事情了,在这里cpu作的事情就是或许已经start了100个线程,因此致使大多数的线程调用的count值仍是0,即temp=0,只有少数的线程完成了count=temp+1的操做,因此输出的count结果不肯定,多是七、八、9,也多是10几。code

 1 def sub():
 2     global count
 3 
 4     '''线程的公共数据  下'''
 5     temp=count
 6     time.sleep(0.001)    #大量的io操做
 7     count=temp+1
 8     '''线程的公共数据  上'''
 9 
10     time.sleep(2)
11 count=0
12 
13 l=[]
14 for i in range(100):
15     t=threading.Thread(target=sub,args=())
16     t.start()
17     l.append(t)
18 for t in l:
19     t.join()
20 print(count)

 

注意如下的锁都是多线程提供的锁机制,与python解释器引入的gil概念无关blog

互斥锁(同步锁)递归

互斥锁是用来解决上述的io密集型场景产生的计算错误,即目的是为了保护共享的数据,同一时间只能有一个线程来修改共享的数据。进程

 1 def sub():
 2     global count
 3     lock.acquire()  #上锁,第一个线程若是申请到锁,会在执行公共数据的过程当中持续阻塞后续线程
 4                     #即后续第二个或其余线程依次来了发现已经被上锁,只能等待第一个线程释放锁
 5                     #当第一个线程将锁释放,后续的线程会进行争抢
 6 
 7     '''线程的公共数据  下'''
 8     temp=count
 9     time.sleep(0.001)
10     count=temp+1
11     '''线程的公共数据  上'''
12 
13     lock.release()  #释放锁
14     time.sleep(2)
15 count=0
16 
17 l=[]
18 lock=threading.Lock()   #将锁内的代码串行化
19 for i in range(100):
20     t=threading.Thread(target=sub,args=())
21     t.start()
22     l.append(t)
23 for t in l:
24     t.join()
25 print(count)

 

死锁

保护不一样的数据就应该加不一样的锁。

因此当有多个互斥锁存在的时候,可能会致使死锁,死锁原理以下:

 1 import threading
 2 import time
 3 def foo():
 4     lockA.acquire()
 5     print('func foo ClockA lock')
 6     lockB.acquire()
 7     print('func foo ClockB lock')
 8     lockB.release()
 9     lockA.release()
10 
11 def bar():
12 
13     lockB.acquire()
14     print('func bar ClockB lock')
15     time.sleep(2)  # 模拟io或者其余操做,第一个线程执行到这,在这个时候,lockA会被第二个进程占用
16                    # 因此第一个进程没法进行后续操做,只能等待lockA锁的释放
17     lockA.acquire()
18     print('func bar ClockA lock')
19     lockB.release()
20     lockA.release()
21 
22 def run():
23     foo()
24     bar()
25 
26 lockA=threading.Lock()
27 lockB=threading.Lock()
28 for i in range(10):
29     t=threading.Thread(target=run,args=())
30     t.start()
31 
32 输出结果:只有四行,由于产生了死锁阻断了
33 func foo ClockA lock
34 func foo ClockB lock
35 func bar ClockB lock
36 func foo ClockA lock

 

 

递归锁(重要)

解决死锁

 1 import threading
 2 import time
 3 def foo():
 4     rlock.acquire()
 5     print('func foo ClockA lock')
 6     rlock.acquire()
 7     print('func foo ClockB lock')
 8     rlock.release()
 9     rlock.release()
10 
11 def bar():
12     rlock.acquire()
13     print('func bar ClockB lock')
14     time.sleep(2)
15     rlock.acquire()
16     print('func bar ClockA lock')
17     rlock.release()
18     rlock.release()
19 
20 
21 def run():
22     foo()
23     bar()
24 
25 rlock=threading.RLock() #RLock自己有一个计数器,若是碰到acquire,那么计数器+1
26                         #若是计数器大于0,那么其余线程没法查收,若是碰到release,计数器-1
27 
28 for i in range(10):
29     t=threading.Thread(target=run,args=())
30     t.start()

 

 

Semaphore(信号量)

实际上也是一种锁,该锁用于限制线程的并发量

如下代码在sleep两秒后会打印出100个ok

1 import threading
2 import time
3 def foo():
4     time.sleep(2)
5     print('ok')
6 
7 for i in range(100):
8     t=threading.Thread(target=foo,args=())
9     t.start()

 

每2秒打印5次ok

 1 import threading
 2 import time
 3 sem=threading.Semaphore(5)
 4 def foo():
 5     sem.acquire()
 6     time.sleep(2)
 7     print('ok')
 8     sem.release()
 9 
10 for i in range(100):
11     t=threading.Thread(target=foo,args=())
12     t.start()
相关文章
相关标签/搜索