多線程 前言 我看了不止一個人說多線程是雞肋,但是就依照我個人覺得多線程在一些小型的爬蟲中還是可以顯著的提高速度的,相比多進程來說應該還是挺簡單的 使用多線程 繼承threading.Thread 繼承threading.Thread模塊是一個很好的一個選擇,就像java中也是可以繼承類和實現介面一 ...
多線程
前言
我看了不止一個人說多線程是雞肋,但是就依照我個人覺得多線程在一些小型的爬蟲中還是可以顯著的提高速度的,相比多進程來說應該還是挺簡單的
使用多線程
繼承threading.Thread
繼承threading.Thread模塊是一個很好的一個選擇,就像java中也是可以繼承類和實現介面一樣,這都是很好的選擇,下麵我們來看看具體如何使用
class Mythread(threading.Thread):
def __init__(self,threadID,name,counter):
threading.Thread.__init__(self) #首先需要先保留原來threading.Thread中的初始化函數
self.threadID=threadID #重命名線程的ID
self.name=name #線程的名字
self.counter=counter #線程的數量
def run(self):
lock.acquire() #獲取線程鎖Lock
for i in range(10):
print "線程"+self.name+"開始運行"
lock.release() #釋放線程鎖Lock
if __name__ == '__main__':
lock=threading.Lock()
t1=Mythread(0,"thread-1",3)
t2=Mythread(1,"thread-2",3)
t1.start()
t2.start()
threads=[]
threads.append(t1)
threads.append(t2)
for t in threads:
t.join() #阻塞主線程,直至線程運行完畢才運行main線程的語句
print "線程運行結束"
需要註意的是,這種繼承的方式有一個缺點,這個和java中繼承來實現多線程是一樣的,就是一個對象只能是對應一個線程,並不能一個對象被多個線程共用,下麵我們將會介紹另外的一種方式
直接調用threading.Thread
上面我們說過繼承的方式,但是我個人覺得對於一些比較小的爬蟲還是有些繁瑣的,因為總是需要重寫run方法,現在我們來看看如何簡化實現多線程
"""
這是一個簡單的例子,其實也不是一個好的例子,但是為了演示方便就選用了,可以看出這裡是直接調用
了func函數,然後變成多個線程同時並行,其中target是要調用的方法(沒有括弧),args是方法調用需要傳入的參數
其實這個還是和上面的繼承比較相似的
"""
def func(name,age):
for i in range(10):
print name+"的年齡為:"+str(age)
t=threading.Thread(target=func,args=["陳加兵",22])
t.start()
Thread對象的相關方法
- start() 啟動線程
- join([timeout]) 設置阻塞線程,timeout是可選的參數,表示阻塞的時間,如果沒有就是當此線程運行結束才開始運行下一個線程
- run() 線程活動的方法
- getName() 獲取線程名稱
- setName() 設置線程的名稱
- isAlive() 判斷線程是否還活著
- isDaemon() 判斷是否是守護線程
- setDaemon() 設置為守護線程,守護線程就是當主線程運行完後,這個線程也會隨著主線程的結束而結束
共用隊列
從源代碼可以看出隊列是實現了鎖原語的,因此可以使用隊列實現線程的同步,這裡的主要原理就不細說了,簡單的說就是get和put等方法都實現了鎖原語,就是當一個操作正在執行的時候其他的操作會阻塞等待
下麵我自己寫了一個使用兩個線程實現同時入隊和出隊的程式
import random
import time
from Queue import Queue
class myThread(threading.Thread):
def __init__(self,threadID,name,counter,q,flag):
"""
threadID是線程的ID
name是線程的名稱
q是先進先出隊列
flag是用來調用get和put的標誌
"""
threading.Thread.__init__(self)
self.name=name
self.threadID=threadID
self.counter=counter
self.q=q
self.flag=flag
def run(self):
"""
當flag為1時就調用put方法,否則調用get
"""
if self.flag==1:
self.put()
else:
self.get()
def put(self):
while True:
self.q.put(random.randint(0,10))
def get(self):
while True:
if not self.q.empty():
print self.q.get()
if __name__=="__main__":
threadLock=threading.Lock()
q=Queue()
t1=myThread(1,"Thread-1",1,q,1)
t2=myThread(2,"Thread-2",2,q,2)
threads=[]
threads.append(t1)
threads.append(t2)
t1.start()
t2.start()
Queue相關的一些方法
- Queue.qsize() 返回隊列的大小
- Queue.empty() 如果隊列為空,返回True,反之False
- Queue.full() 如果隊列滿了,返回True,反之False
- Queue.full 與 maxsize 大小對應
- Queue.get([block[, timeout]])獲取隊列,timeout等待時間
- Queue.get_nowait() 相當Queue.get(False)
- Queue.put(item) 寫入隊列,timeout等待時間
- Queue.put_nowait(item) 相當Queue.put(item, False)
- Queue.task_done() 在完成一項工作之後, Queue.task_done()函數向任務已經完成的隊列發送一個信號
- Queue.join() 實際上意味著等到隊列為空,再執行別的操作