如果多個線程共同對某個資料修改,則可能出現不可預料的結果,為了保證資料的正確性,需要對多個線程進行同步。
1 簡單的線程同步。
使用Thread對象的Lock和Rlock可以實現簡單的線程同步,這兩個對象都有acquire方法和release方法,對於那些需要每次只允許一個線程操作的資料,可以將其操作放到acquire和release方法之間。如下:
import threading
import time
class mythread(threading.Thread):
def__init__(self,threadname):
threading.Thread.__init__(self,name=threadname)
defrun(self):
global x
lock.acquire()
for i in range(3):
x=x+1
time.sleep(2)
print x
lock.release()
lock=threading.RLock()
t1=[]
for i in range(10):
t=mythread(str(i))
t1.append(t)
x=0
for i in t1:
i.start()
運行結果如下:
>>> 3
6
9
12
15
18
21
24
27
30
>>>
而如果我們把acquire()和release()去掉,結果就不同了:
30303030303030303030
這是因為每個線程執行後在列印出x之前都要休眠2秒鐘,所以在這個過程中,每個線程都被執行了,所以等到休眠結束,列印出的X的值自然就是經過多次運算以後的X的值了。
而第一次,我們把全域變數X放到了acquire()和release()之間,python解譯器每次回只允許一個線程對x進行操作,只有這個線程結束對其操作並且休眠結束列印出來以後,才允許下一個線程對x操作,所以輸出的X是每次遞增的,而且用時間也是比較長的。
2 使用條件變數保持線程同步
Python的Condition對象提供了對複雜線程同步的支援,使用它可以在某些事件觸發之後才處理資料,condition的方法有很多,看下面的例子:
用Condition來實現著名的生產者和消費者的關係:
import threading
class Producer(threading.Thread):
def__init__(self,threadname):
threading.Thread.__init__(self,name=threadname)
defrun(self):
global x
con.acquire()
if x==1000000:
con.wait()
pass
else:
for i in range(1000000):
x=x+1
con.notify()
print x
con.release()
class Consumer(threading.Thread):
def__init__(self,threadname):
threading.Thread.__init__(self,name=threadname)
defrun(self):
global x
con.acquire()
if x==0:
con.wait()
pass
else:
for i in range(1000000):
x=x-1
con.notify()
print x
con.release()
con=threading.Condition()
x=0
p=Producer('Producer')
c=Consumer('Consumer')
p.start()
c.start()
p.join()
c.join()
print x
結果如下:
>>>
1000000
0
0
>>>
3 使用隊列保持線程同步
同樣的例子,使用Queue可以實現多生產者和多消費者的先進先出的隊列,每個生產者將資料依次存入隊列,而每個消費者則依次從隊列中取出資料,看下面的例子:
import threading
import time
import Queue
class Producer(threading.Thread):
def__init__(self,threadname):
threading.Thread.__init__(self,name=threadname)
defrun(self):
global queue
queue.put(self.getName())
print self.getName(),'put',self.getName(),'to queue'
class Consumer(threading.Thread):
def__init__(self,threadname):
threading.Thread.__init__(self,name=threadname)
defrun(self):
global queue
print self.getName(),'get',self.getName(),'from queue'
queue=Queue.Queue()
plist=[]
clist=[]
for i in range(10):
p=Producer('Producer'+str(i))
plist.append(p)
for i in range(10):
c=Consumer('Consumer'+str(i))
clist.append(c)
for i in plist:
i.start()
i.join()
for i in clist:
i.start()
i.join()
看結果:
>>>
Producer0 put Producer0 to queue
Producer1 put Producer1 to queue
Producer2 put Producer2 to queue
Producer3 put Producer3 to queue
Producer4 put Producer4 to queue
Producer5 put Producer5 to queue
Producer6 put Producer6 to queue
Producer7 put Producer7 to queue
Producer8 put Producer8 to queue
Producer9 put Producer9 to queue
Consumer0 get Consumer0 from queue
Consumer1 get Consumer1 from queue
Consumer2 get Consumer2 from queue
Consumer3 get Consumer3 from queue
Consumer4 get Consumer4 from queue
Consumer5 get Consumer5 from queue
Consumer6 get Consumer6 from queue
Consumer7 get Consumer7 from queue
Consumer8 get Consumer8 from queue
Consumer9 get Consumer9 from queue