好久沒有更新博文啦,在家過春節已經變懶了-_-,不過答應你們更完這個python的入門系列,偶仍是會繼續努力的!另外祝願你們新年快樂,事事順心!java
咱們學習的不少編程語言,好比java,oc等,都會有線程這個概念.線程的用途很是的普遍,給咱們開發中帶來了不少的便利.主要用於一些串行或者並行的邏輯處理,好比點擊某個按鈕的時候,咱們能夠經過進度條來控制線程的運行時間,以便於更好的用於用戶的交互.python
每一個獨立的線程都包含一個程序的運行入口,順序的執行序列和一個程序運行的出口.線程必須在程序中存在,而不能獨立於程序運行!編程
每一個線程都有他本身的一組cpu儲存器,稱爲線程的上下文,該上下文反應了線程上次運行的cpu寄存器的狀態.指令指針和堆棧指針寄存器是線程上下文中兩個最重要的寄存器,線程老是在進程獲得上下文運行,這些地址都用於標誌擁有線程的進程地址空間中的內存.數組
在Python中,主要提供了thread和threading兩個線程模塊,thread模塊提供了最基礎的,最低級的線程函數,和一個簡單的鎖.threading模塊是thread模塊的封裝進階,提供了多樣的線程屬性和方法.下面咱們會對該兩個模塊逐個解析.安全
thread模塊經常使用的函數方法:多線程
函數名 | 描述 |
---|---|
start_new_thread(function, args, kwargs=None) | 產生一個新線程,function爲線程要運行的函數名,args是函數參數(tuple元組類型),kwargs爲可選參數 |
allocate_lock() | 分配一個locktype類型的線程鎖對象 |
exit() | 線程退出 |
_count() | 返回線程數量,注意不包含主線程哦,因此在主線程運行該方法返回的是0 |
locked | locktype 鎖,返回true爲已鎖 |
release() | 釋放locktype對象鎖 |
acquire() | 鎖定 |
下面咱們來舉個例子:編程語言
import thread,time
def loop1():
print '線程個數-' + str(thread._count())
i=0
try:
while i < 100:
print i
time.sleep(1)
i = i + 1
except Exception as e:
print e
thread.start_new_thread(loop1,())
複製代碼
運行上面代碼,你會發現loop1方法中的循環打印並無被調用,而是直接返回了一個異常:函數
Unhandled exception in thread started by
sys.excepthook is missing
lost sys.stderr
複製代碼
這時你可能會一遍又一遍的檢查代碼,覺得是代碼錯了(沒錯,那我的就是我),其實咱們代碼自己是沒有錯誤的,是早期python的thread模塊一個缺陷(這個缺陷也是致使這個模塊被官方不推薦使用的主要緣由):當咱們在主線程中使用start_new_thread建立新的線程的時候,主線程沒法得知線程什麼時候結束,他不知道要等待多久,致使主線程已經執行完了,子線程卻還未完成,因而系統拋出了這個異常.oop
解決這個異常的方法有兩種:學習
1.讓主線程休眠足夠長的時間來等待子線程返回結果:
import thread,time
def loop1():
print '線程個數-' + str(thread._count())
i=0
try:
while i < 100:
print i
time.sleep(1)
i = i + 1
except Exception as e:
print e
thread.start_new_thread(loop1,())
time.sleep(1000) #讓主線程休眠1000秒,足夠子線程完成
複製代碼
2.給線程加鎖(早期python線程使用通常處理)
import thread,time
def loop1(lock):
print '線程個數-' + str(thread._count())
i=0
try:
while i < 100:
print i
time.sleep(1)
i = i + 1
except Exception as e:
lock.release()
print e
lock.release() #執行完畢,釋放鎖
lock=thread.allocate_lock() #獲取locktype對象
lock.acquire() #鎖定
thread.start_new_thread(loop1,(lock,))
while lock.locked(): #等待線程鎖釋放
pass
複製代碼
以上就是thread模塊的經常使用線程用法,咱們能夠看出,thread模塊提供的線程操做是極其有限的,使用起來很是的不靈活,下面咱們介紹他的同胞模塊threading.
threading模塊是thread的完善,有一套成熟的線程操做方法,基本能完成咱們所需的全部線程操做
threading經常使用方法:
threading模塊建立線程有兩種方式:
1.直接經過初始化thread對象建立:
#coding=utf-8
import threading,time
def test():
t = threading.currentThread() # 獲取當前子線程對象
print t.getName() # 打印當前子線程名字
i=0
while i<10:
print i
time.sleep(1)
i=i+1
m=threading.Thread(target=test,args=(),name='循環子線程') #初始化一個子線程對象,target是執行的目標函數,args是目標函數的參數,name是子線程的名字
m.start()
t=threading.currentThread() #獲取當前線程對象,這裏實際上是主線程
print t.getName() #打印當前線程名字,實際上是主線程名字
複製代碼
能夠看到打印結果:
循環子線程
MainThread
0
1
2
3
4
5
6
7
8
複製代碼
2.經過基礎thread類來建立
import threading,time
class myThread (threading.Thread): #建立一個自定義線程類mythread,繼承Thread
def __init__(self,name):
"""
從新init方法
:param name: 線程名
"""
super(myThread, self).__init__(name=name)
# self.lock=lock
print '線程名'+name
def run(self):
"""
從新run方法,這裏面寫咱們的邏輯
:return:
"""
i=0
while i<10:
print i
time.sleep(1)
i=i+1
if __name__=='__main__':
t=myThread('mythread')
t.start()
複製代碼
輸出:
線程名線程
0
1
2
3
4
5
6
7
8
9
複製代碼
若是兩個線程同時訪問同一個數據的時候,可能會出現沒法預料的結果,這時候咱們就要用到線程同步的概念.
上面咱們講到thread模塊的時候,已經使用了線程鎖的概念,thread對象的Lock和Rlock能夠實現簡單的線程同步,這兩個對象都有acquire方法和release方法,對於那些須要每次只容許一個線程操做的數據,能夠將其操做放到acquire和release方法之間.
下面咱們來舉例說明,咱們須要實現3個線程同時訪問一個全局變量,而且改變這個變量:
import threading,time
lock=threading.Lock() #全局的鎖對象
temp=0 #咱們要多線程訪問的全局屬性
class myThread (threading.Thread): #建立一個自定義線程類mythread,繼承Thread
def __init__(self,name):
"""
從新init方法
:param name: 線程名
"""
super(myThread, self).__init__(name=name)
# self.lock=lock
print '線程名'+name
def run(self):
"""
從新run方法,這裏面寫咱們的邏輯
:return:
"""
global temp,lock
i=0
while i<2: #這裏循環兩次累加全局變量,目的是增長出錯的機率
temp=temp+1 #在子線程中實現對全局變量加1
print self.name+'--temp=='+str(temp)
i=i+1
if __name__=='__main__':
t1=myThread('線程1')
t2=myThread('線程2')
t3=myThread('線程3')
#建立三個線程去執行訪問
t1.start()
t2.start()
t3.start()
複製代碼
執行結果(因爲程序運行很快,你多運行幾回就可能會出現如下結果): 咱們能夠發現,線程1和線程2同時訪問到了變量,致使打印出現對等狀況
線程名線程1
線程名線程2
線程名線程3
線程1--temp==1線程2--temp==2
線程1--temp==3
線程2--temp==4
線程3--temp==5
線程3--temp==6
複製代碼
import threading,time
lock=threading.Lock() #全局的鎖對象
temp=0 #咱們要多線程訪問的全局屬性
class myThread (threading.Thread): #建立一個自定義線程類mythread,繼承Thread
def __init__(self,name):
"""
從新init方法
:param name: 線程名
"""
super(myThread, self).__init__(name=name)
# self.lock=lock
print '線程名'+name
def run(self):
"""
從新run方法,這裏面寫咱們的邏輯
:return:
"""
global temp,lock
if lock.acquire(): #這裏線程進來訪問變量的時候,鎖定變量
i = 0
while i < 2: # 這裏循環兩次累加全局變量,目的是增長出錯的機率
temp = temp + 1 # 在子線程中實現對全局變量加1
print self.name + '--temp==' + str(temp)
i = i + 1
lock.release() #訪問完畢,釋放鎖讓另外的線程訪問
if __name__=='__main__':
t1=myThread('線程1')
t2=myThread('線程2')
t3=myThread('線程3')
#建立三個線程去執行訪問
t1.start()
t2.start()
t3.start()
複製代碼
運行結果(無論運行多少次,都不會出現同時訪問的狀況):
線程名線程1
線程名線程2
線程名線程3
線程1--temp==1
線程1--temp==2
線程2--temp==3
線程2--temp==4
線程3--temp==5
線程3--temp==6
複製代碼
線程同步不少地方都會用到,好比搶票,抽獎,咱們須要對一些資源進行鎖定,以防止多線程訪問的時候出現不可預知的狀況.
python中的隊列用到了Queue模塊,該模塊提供了同步的,安全的對序列,包括FIFO(先入先出)隊列Queue,LIFO(後入先出)隊列LifoQueue,和優先級隊列PriorityQueue.這些隊列都實現了鎖原語,可以在多線程中直接使用。可使用隊列來實現線程間的通訊
Queue模塊中的經常使用方法:
例子:
tags=['one','tow','three','four','five','six']
q=Queue.LifoQueue() #先入先出隊列
for t in tags:
q.put(t) #將數組數據加入隊列
for i in range(6):
print q.get() #取出操做能夠放在不一樣的線程中,不會出現同步的問題
複製代碼
結果:
six
five
four
three
tow
one
複製代碼
這章的多線程就到這裏了,咱們主要講述了他的基本用法,更多的用法咱們能夠在之後的開發過程當中,根據本身邏輯去設計.