python多線程模塊thread,threading,Queue

python通過兩個標準庫(thread, threading)提供了對多線程的支持

thread模塊

import time
import thread

def runner(arg):
    for i in range(6):
        print str(i)+':'+arg

        time.sleep(1)
    #結束當前線程
    thread.exit_thread()  #等同於thread.exit()
       
#啓動一個線程,第一個參數爲函數名,
#第二個參數爲一個tuple類型,是傳給函數的參數
thread.start_new_thread(runner, ('hello world',))   #等同於thread.start_new(runner, ('hello world'))

#創建一個鎖,鎖用於線程同步,通常控制對共享資源的訪問
lock = thread.allocate_lock()  #等同於thread.allocate()
num = 0
#獲得鎖,成功返回True,失敗返回False
if lock.acquire():
    num += 1
    #釋放鎖
    lock.release()
#thread模塊提供的線程都將在主線程結束後同時結束,因此將主線程延遲結束
time.sleep(10)
print 'num:'+str(num)
threading.Thread類的常用方法
1.在自己的線程類的__init__裏調用threading.Thread.__init__(self, name=threadname),threadname爲線程的名字。
2.run(),通常需要重寫,編寫代碼實現做需要的功能。
3.getName(),獲得線程對象名稱。
4.setName(),設置線程對象名稱。
5.start(),啓動線程。
6.join([timeout]),等待另一線程結束後再運行。
7.setDaemon(bool),設置子線程是否隨主線程一起結束,必須在start()之前調用。默認爲False。
8.isDaemon(),判斷線程是否隨主線程一起結束。
9.isAlive(),檢查線程是否在運行中。

要想創建一個線程對象,只要繼承類threading.Thread,然後在__init__裏邊調用threading.Thread.__init__()方法即可。重寫run()方法,將要實現的功能放到此方法中即可。

class runner(threading.Thread):
	def __init__(self, name):
		threading.Thread.__init__(self)
		self.name = name
		self.thread_stop = False
	def run(self):
		while not self.thread_stop:
			print str(self.name)+':'+'hello world'
			time.sleep(1)
	def stop(self):
		self.thread_stop = True

def test():
	t = runner('thread')
	t.start()
	time.sleep(10)
	t.stop()

if __name__ == '__main__':
	test()

線程同步(鎖)

最簡單的同步機制就是鎖。鎖對象由threading.RLock類創建。
線程可以使用鎖的acquire()方法獲得鎖,這樣鎖就進入locked狀態。每次只有一個線程可以獲得鎖。
如果當另一個線程試圖獲得這個鎖的時候,就會被系統變爲blocked狀態,直到那個擁有鎖的線程調用鎖的release()方法來釋放鎖,這樣鎖就會進入unlocked狀態。blocked狀態的線程就會收到一個通知,並有權利獲得鎖。

如果多個線程處於blocked狀態,所有線程都會先解除blocked狀態,然後系統選擇一個線程來獲得鎖,其他的線程繼續blocked。
python的threading module是在建立在thread module基礎之上的一個module,
在thread module中,python提供了用戶級的線程同步工具Lock對象。
而在threading module中,python又提供了Lock對象的變種: RLock對象。
RLock對象內部維護着一個Lock對象,它是一種可重入的對象。
對於Lock對象而言,如果一個線程連續兩次進行acquire操作,那麼由於第一次acquire之後沒有release,第二次acquire將掛起線程。
這會導致Lock對象永遠不會release,使得線程死鎖。
RLock對象允許一個線程多次對其進行acquire操作,因爲在其內部通過一個counter變量維護着線程acquire的次數。
而且每一次的acquire操作必須有一個release操作與之對應,在所有的release操作完成之後,別的線程才能申請該RLock對象。

我們把修改共享數據的代碼稱爲"臨界區"。必須將所有"臨界區"都封閉在同一個鎖對象的acquire和release之間。

import time
import threading
num=0
lock = threading.RLock()
class runner(threading.Thread):
	def __init__(self, name):
		threading.Thread.__init__(self)
		self.name = name

	def run(self):
		global num  
		while True: 
			if num >= 6: break
			if lock.acquire():
				print "Thread(%s) locked, Number: %d" % (self.name, num)  
				time.sleep(1)
			lock.release()
			print "Thread(%s) released, Number: %d" % (self.name, num)
			time.sleep(1)
			num += 1 
			
def test():
	t1 = runner('thread1')
	t2 = runner('thread2')
 	t1.start()
	t2.start()
 
if __name__== '__main__':  
	test()  
線程同步(條件變量)
鎖只能提供最基本的同步。假如只在發生某些事件時才訪問一個"臨界區",這時需要使用條件變量Condition。
Condition(條件變量)通常與一個鎖關聯。需要在多個Contidion中共享一個鎖時,可以傳遞一個Lock/RLock實例給構造方法,否則它將自己生成一個RLock實例。
在Condition對象上,當然也可以調用acquire和release操作,因爲內部的Lock對象本身就支持這些操作。
但是Condition的價值在於其提供的wait和notify的語義。
條件變量的工作原理?
首先一個線程成功獲得一個條件變量後,調用此條件變量的wait()方法會導致這個線程釋放這個鎖,並進入“blocked”狀態.
直到另一個線程調用同一個條件變量的notify()方法來喚醒那個進入“blocked”狀態的線程。
如果調用這個條件變量的notifyAll()方法的話就會喚醒所有的在等待的線程。
如果程序或者線程永遠處於“blocked”狀態的話,就會發生死鎖。
所以如果使用了鎖、條件變量等同步機制的話,一定要注意仔細檢查,防止死鎖情況的發生。
對於可能產生異常的臨界區要使用異常處理機制中的finally子句來保證釋放鎖。
等待一個條件變量的線程必須用notify()方法顯式的喚醒,否則就永遠沉默。
保證每一個wait()方法調用都有一個相對應的notify()調用,當然也可以調用notifyAll()方法以防萬一。

import threading
import time
con = threading.Condition()
product = None
class Producer(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
    def run(self):
        global product
        if con.acquire():
            while True:
                if product is None:
                    print 'produce...'
                    product = 'anything'
                #通知消費者,商品已經生產
                con.notify()
                #等待通知
                con.wait()
                time.sleep(5)

class Consumer(threading.Thread): 
    def __init__(self):
        threading.Thread.__init__(self) 
    def run(self):
        global product
        if con.acquire():
            while True:
                if product is not None:
                    print 'consume...'
                    product = None
                #通知生產者,商品已經沒了
                con.notify()
                #等待通知
                con.wait()
                time.sleep(5)

def test():
	t1 = Producer()
	t2 = Consumer()
	t1.start()
	t2.start()

if __name__ == '__main__':
	test()
同步隊列

Queue模塊實現了一個支持多producer和多consumer的FIFO隊列。當共享信息需要安全的在多線程之間交換時,Queue非常有用。Queue的默認長度是無限的,但是可以設置其構造函數的maxsize參數來設定其長度。Queue的put方法在隊尾插入,該方法的原型是:

put( item[, block[, timeout]])

如果可選參數block爲true並且timeout爲None(缺省值),線程被block,直到隊列空出一個數據單元。如果timeout大於0,在timeout的時間內,仍然沒有可用的數據單元,Full exception被拋出。反之,如果block參數爲false(忽略timeout參數),item被立即加入到空閒數據單元中,如果沒有空閒數據單元,Full exception被拋出。Queue的get方法是從隊首取數據,其參數和put方法一樣。如果block參數爲true且timeout爲None(缺省值),線程被block,直到隊列中有數據。如果timeout大於0,在timeout時間內,仍然沒有可取數據,Empty exception被拋出。反之,如果block參數爲false(忽略timeout參數),隊列中的數據被立即取出。如果此時沒有可取數據,Empty exception也會被拋出。

import time
import threading
from Queue import Queue  

class Producer(threading.Thread):
    def __init__(self, t_name, queue):
        threading.Thread.__init__(self, name=t_name)
        self.data = queue

    def run(self):
        for i in range(6):
            print "%s: %s is producing %d to the queue!\n" % (time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), self.getName(), i)
			#將值放入隊列
            self.data.put(i)
            time.sleep(1)
        print "%s: %s finished!" % (time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), self.getName())    

class Consumer(threading.Thread):
	def __init__(self, t_name, queue):
		threading.Thread.__init__(self, name=t_name)
		self.data = queue

	def run(self):
		for i in range(6):
			#從隊列中取值
			val = self.data.get()
			print "%s: %s is consuming. %d in the queue is consumed!\n" % (time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), self.getName(), val) 
			time.sleep(1)
		print "%s: %s finished!" % (time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), self.getName())

def test():
	queue = Queue()
	producer = Producer('Producer', queue)
	consumer = Consumer('Consumer', queue)
	producer.start()
	consumer.start()

if __name__ == '__main__':
    test()




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章