單線程演示:python
1.線程就比如<進程線程.形象的說明進程和線程的區別?>中所說的工廠的工人,一個工人幹一個任務叫作單線程
git
2.以下單個線程去訪問4個不一樣的URL,要求返回URL地址和返回碼
github
#!/usr/bin/env python # -*- coding: utf-8 -*- """ # # Authors: limanman # OsChina: http://my.oschina.net/pydevops/ # Purpose: # """ import time import urllib2 def get_response(url_list): """Get url resp code. :param url_list: url list :return: """ sta_time = time.time() for cur_url in url_list: print cur_url, resp = urllib2.urlopen(cur_url) print resp.getcode() print 'cost time: %s' % (time.time()-sta_time) if __name__ == '__main__': url_list = [ 'https://www.baidu.com/', 'https://github.com/', 'http://www.aliyun.com/', 'https://www.dnspod.cn/', ] # 調用執行 get_response(url_list)
https://www.baidu.com/ 200 https://github.com/ 200 http://www.aliyun.com/ 200 https://www.dnspod.cn/ 200 cost time: 7.49797701836
說明:如上4個URL被順序請求,除非CPU從一個URL得到了迴應,不然不會去請求下一個URL,網絡請求會花費較長時間,因此CPU在等待網絡請求的返回時間內會一直處於閒置狀態安全
多線程演示:網絡
1.線程就比如<進程線程.形象的說明進程和線程的區別?>中所說的工廠的工人,多個工人幹一個任務叫作多線程
多線程
2.以下多個線程去訪問4個不一樣的URL,要求返回URL地址和返回碼app
#!/usr/bin/env python # -*- coding: utf-8 -*- """ # # Authors: limanman # OsChina: http://my.oschina.net/pydevops/ # Purpose: # """ import time import urllib2 import threading class MultThread(threading.Thread): def __init__(self, url): self.url = url super(MultThread, self).__init__() def run(self): print self.url, resp = urllib2.urlopen(self.url, data=None, timeout=3) print resp.getcode() def get_response(url_list): """Get resp with mulit threads. :param url_list: url list :return: """ # 建立url_list長度個線程 sta_time = time.time() thread_list = [] for cur_url in url_list: cur_thread = MultThread(cur_url) thread_list.append(cur_thread) # 通知CPU能夠執行MultThread下面的run方法 cur_thread.start() # 保證全部線程執行完畢後再執行print,不然會先執行print而後再回頭繼續執行線程run方法 for cur_thread in thread_list: cur_thread.join() print 'cost time: %s' % (time.time()-sta_time) if __name__ == '__main__': url_list = [ 'https://www.baidu.com/', 'https://github.com/', 'http://www.aliyun.com/', 'https://www.dnspod.cn/', ] # 調用 get_response(url_list)
https://www.baidu.com/ https://github.com/ https://www.dnspod.cn/http://www.aliyun.com/ 200 200 200 200 cost time: 1.33794403076
說明:從執行時間上面已經明顯感受有性能上提高,如上是一個多線程減小CPU等待時間,在等待一個線程內的網絡請求返回時,CPU能夠切換到其它線程去進行其它線程內的網絡請求,因此你會發現打印的格式是先把全部的url打印出來,而後等待返回碼,返回一個打印一個,咱們指望一個線程處理一個url,固然也能夠一個線程處理多個url,因此實例化線程類咱們傳入一個url,線程運行意味着執行類裏的run()方法,性能
因此爲每一個url建立一個線程並調用start()方法,告訴CPU能夠執行線程中的run()方法了,咱們但願全部的線程都執行完畢後再計算執行時間,因此咱們對每一個線程調用join()方法,它會通知主線程等待這個線程結束後,纔會執行下一條指令,也就是print那句,須要注意的是CPU可能不會在調用start()後立刻執行run()方法,沒法肯定run()在不一樣線程間的執行順序,對於單獨的一個線程能夠保證順序執行,由於線程內的url會依次被請求並返回結果ui
全局變量線程安全問題:url
1.在<進程線程.形象的說明進程和線程的區別?>中說,車間的空間是工人們共享的,好比許多房間裏是每一個工人均可以進出的,這就象徵一個進程的內存空間是共享的,每一個線程均可以使用這些共享內存
2.在<進程線程.形象的說明進程和線程的區別?>中說,每一個房間的大小不一樣,有些房間只能容納一我的,好比廁所,裏面有人的時候其餘人不能進,這表明一個線程使用某些共享內存時,其它線程必須等待它結束,才能使用這一起內存
#!/usr/bin/env python # -*- coding: utf-8 -*- """ # # Authors: limanman # OsChina: http://my.oschina.net/pydevops/ # Purpose: # """ import time import threading class MultThread(threading.Thread): def __init__(self): super(MultThread, self).__init__() def run(self): global global_num currentnum = global_num print 'thread - %s currentnum - %s' % (self.name, currentnum) global_num = currentnum + 1 if __name__ == '__main__': # 全局變量 global_num = 0 thread_list = [] # 開啓50個線程 for _ in xrange(50): cur_thread = MultThread() thread_list.append(cur_thread) cur_thread.start() for cur_thread in thread_list: cur_thread.join() print print 'global_num modify 50 times, should become 50.' print 'after 50 times modify, global_num is %s.' % (global_num)
thread - Thread-1 currentnum - 0 thread - Thread-2 currentnum - 1 thread - Thread-3 currentnum - 2 thread - Thread-4 currentnum - 3 thread - Thread-5 currentnum - 4 thread - Thread-6 currentnum - 5 thread - Thread-7 currentnum - 6 thread - Thread-8 currentnum - 7 thread - Thread-9 currentnum - 8thread - Thread-10 currentnum - 8 thread - Thread-11 currentnum - 9 thread - Thread-12 currentnum - 10 thread - Thread-13 currentnum - 11 thread - Thread-14 currentnum - 9 thread - Thread-15 currentnum - 10 thread - Thread-16 currentnum - 11 thread - Thread-17 currentnum - 12 thread - Thread-18 currentnum - 13 thread - Thread-19 currentnum - 14 thread - Thread-20 currentnum - 15 thread - Thread-21 currentnum - 16 thread - Thread-22 currentnum - 17 thread - Thread-23 currentnum - 18 thread - Thread-24 currentnum - 19 thread - Thread-25 currentnum - 20 thread - Thread-26 currentnum - 21thread - Thread-27 currentnum - 21 thread - Thread-28 currentnum - 22 thread - Thread-29 currentnum - 23 thread - Thread-30 currentnum - 24 thread - Thread-31 currentnum - 25 thread - Thread-32 currentnum - 26 thread - Thread-33 currentnum - 27 thread - Thread-34 currentnum - 28 thread - Thread-35 currentnum - 29 thread - Thread-36 currentnum - 30 thread - Thread-37 currentnum - 31 thread - Thread-38 currentnum - 32 thread - Thread-39 currentnum - 33 thread - Thread-40 currentnum - 34 thread - Thread-41 currentnum - 35 thread - Thread-42 currentnum - 36 thread - Thread-43 currentnum - 37 thread - Thread-44 currentnum - 38 thread - Thread-45 currentnum - 39 thread - Thread-46 currentnum - 40 thread - Thread-47 currentnum - 41 thread - Thread-48 currentnum - 42 thread - Thread-49 currentnum - 43 thread - Thread-50 currentnum - 44 global_num modify 50 times, should become 50. after 50 times modify, global_num is 45.
說明:有一個全局變量,全部的線程都想修改它,全部的線程應該在這個全局變量的基礎上加1,有50個線程,最後這個數值應該變爲50,爲何沒有到50?當global_num是8的時候,Thread-10讀取了global_num,這個時刻cpu將控制權給了另外一個線程Thread-10,Thread-10讀取到的也是8,Thread-9和Thread-10都把global_num加到9,可是咱們指望的是Thread-9和Thread-10兩個線程使global_num+2變爲10,也就是Thread-11爲10,在這裏有了資源競爭,相同的狀況也會發生在其它線程之間,因此出現了最後的結果小於50的狀況
解決資源競爭:
1.在<進程線程.形象的說明進程和線程的區別?>中說一個防止他進入的簡單方法,就是門口加一把鎖,先到的人鎖上門,後到的人看到上鎖,就在門口排隊,等鎖打開再進去,這就叫"互斥鎖",防止多個線程同時讀寫某一塊內存區域
#!/usr/bin/env python # -*- coding: utf-8 -*- """ # # Authors: limanman # OsChina: http://my.oschina.net/pydevops/ # Purpose: # """ import time import threading class MultThread(threading.Thread): def __init__(self, thread_lock): super(MultThread, self).__init__() self.thread_lock = thread_lock def run(self): global global_num # 獲取一把鎖 self.thread_lock.acquire() currentnum = global_num print 'thread - %s currentnum - %s' % (self.name, currentnum) global_num = currentnum + 1 # 釋放這把鎖 self.thread_lock.release() if __name__ == '__main__': # 全局變量 global_num = 0 # 互斥鎖 lock = threading.Lock() thread_list = [] # 開啓50個線程 for _ in xrange(50): cur_thread = MultThread(lock) thread_list.append(cur_thread) cur_thread.start() # 等待全部線程結束再執行下面的指令 for cur_thread in thread_list: cur_thread.join() print print 'global_num modify 50 times, should become 50.' print 'after 50 times modify, global_num is %s.' % (global_num)
thread - Thread-1 currentnum - 0 thread - Thread-2 currentnum - 1 thread - Thread-3 currentnum - 2 thread - Thread-4 currentnum - 3 thread - Thread-5 currentnum - 4 thread - Thread-6 currentnum - 5 thread - Thread-7 currentnum - 6 thread - Thread-8 currentnum - 7 thread - Thread-9 currentnum - 8 thread - Thread-10 currentnum - 9 thread - Thread-11 currentnum - 10 thread - Thread-12 currentnum - 11 thread - Thread-13 currentnum - 12 thread - Thread-14 currentnum - 13 thread - Thread-15 currentnum - 14 thread - Thread-16 currentnum - 15 thread - Thread-17 currentnum - 16 thread - Thread-18 currentnum - 17 thread - Thread-19 currentnum - 18 thread - Thread-20 currentnum - 19 thread - Thread-21 currentnum - 20 thread - Thread-22 currentnum - 21 thread - Thread-23 currentnum - 22 thread - Thread-24 currentnum - 23 thread - Thread-25 currentnum - 24 thread - Thread-26 currentnum - 25 thread - Thread-27 currentnum - 26 thread - Thread-28 currentnum - 27 thread - Thread-29 currentnum - 28 thread - Thread-30 currentnum - 29 thread - Thread-31 currentnum - 30 thread - Thread-32 currentnum - 31 thread - Thread-33 currentnum - 32 thread - Thread-34 currentnum - 33 thread - Thread-35 currentnum - 34 thread - Thread-36 currentnum - 35 thread - Thread-37 currentnum - 36 thread - Thread-38 currentnum - 37 thread - Thread-39 currentnum - 38 thread - Thread-40 currentnum - 39 thread - Thread-41 currentnum - 40 thread - Thread-42 currentnum - 41 thread - Thread-43 currentnum - 42 thread - Thread-44 currentnum - 43 thread - Thread-45 currentnum - 44 thread - Thread-46 currentnum - 45 thread - Thread-47 currentnum - 46 thread - Thread-48 currentnum - 47 thread - Thread-49 currentnum - 48 thread - Thread-50 currentnum - 49 global_num modify 50 times, should become 50. after 50 times modify, global_num is 50.
說明:Lock用來防止競爭條件,達到咱們預想的結果,若是在執行前thread-1得到了鎖,其它線程在t1釋放Lock以前,不會執行相同的操做,咱們想要肯定的是一旦Thread-1已經讀取了global_num,直到完成了修改global_num,其它線程才能夠讀取和修改global_num
線程切換輸出打斷:
#!/usr/bin/env python # -*- coding: utf-8 -*- """ # # Authors: limanman # OsChina: http://my.oschina.net/pydevops/ # Purpose: # """ import time import threading class MultThread(threading.Thread): def __init__(self): super(MultThread, self).__init__() def run(self): self.entries = [] for i in xrange(10): # 強制CPU切換到其它線程 time.sleep(0.01) self.entries.append(i) # 打印會亂掉,可是也說明一個問題線程之間變量是相互獨立的 print self.entries if __name__ == '__main__': thread_list = [] for _ in xrange(5): cur_thread = MultThread() thread_list.append(cur_thread) cur_thread.start()
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [0[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] , 1, 2, 3, 4, 5, 6, 7, 8, 9]
說明:上面用了一個time.sleep()來讓一個線程掛起,強制切換到其它線程,可是並無和咱們指望同樣打印正確結果,當一個線程正在打印時,CPU切換到另外一個線程,因此產生了不正確的結果,咱們須要保證print語句的原子操做,防止打印時被其它線程打斷
保證線程原子操做:
#!/usr/bin/env python # -*- coding: utf-8 -*- """ # # Authors: limanman # OsChina: http://my.oschina.net/pydevops/ # Purpose: # """ import time import threading class MultThread(threading.Thread): def __init__(self, thread_lock): self.thread_lock = thread_lock super(MultThread, self).__init__() def run(self): self.entries = [] for i in xrange(10): # 強制CPU切換到其它線程 time.sleep(0.01) self.entries.append(i) # 打印會亂掉,可是也說明一個問題線程之間變量是相互獨立的 self.thread_lock.acquire() print self.entries self.thread_lock.release() if __name__ == '__main__': # 互斥鎖 lock = threading.Lock() thread_list = [] for _ in xrange(5): cur_thread = MultThread(lock) thread_list.append(cur_thread) cur_thread.start()
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
說明:此次咱們看到了正確結果,同時也說明了一個問題,一個線程不能夠修改其它線程內部的變量(全局變量除外)
強制線程退出:
#!/usr/bin/env python # -*- coding: utf-8 -*- """ # # Authors: limanman # OsChina: http://my.oschina.net/pydevops/ # Purpose: # """ import time import threading class MultThread(threading.Thread): def __init__(self, thread_lock): self.thread_lock = thread_lock super(MultThread, self).__init__() def run(self): self.entries = [] for i in xrange(10): # 強制CPU切換到其它線程 time.sleep(0.01) self.entries.append(i) # 打印會亂掉,可是也說明一個問題線程之間變量是相互獨立的 self.thread_lock.acquire() print self.entries self.thread_lock.release() if __name__ == '__main__': # 互斥鎖 lock = threading.Lock() thread_list = [] for _ in xrange(5): cur_thread = MultThread(lock) thread_list.append(cur_thread) # 將主線程設置爲守護線程,主線程結束後,無論子線程是否完成都一併和主線程退出 cur_thread.setDaemon(True) cur_thread.start() # 當前線程數若是不等於1,也就是除了守護線程還有其它線程在運行 while threading.activeCount() != 1: # 強制切換到守護線程外的其餘線程運行 time.sleep(0.01) print 'found notice: all thread is finished.'
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] found notice: all thread is finished.
說明: 能夠看出Thread-[1-5]的內容打印都沒有打印出來,cur_thread.setDaemon(True)的操做將父線程設置爲了守護線程,根據setDaemon()方法的含義,守護線程由於不會像join()那樣等待子線程結束後執行下面的語句,因此守護線程會先執行print 'found notice: all thread is finished.'而後就結束了,全部子線程也就結束了,可是爲了實現join()的效果,因此在上面能夠加一個判斷,判斷包含守護線程在內的線程數是否等於1,若是等於1則表示全部的線程已經所有結束.
容許指定線程數更改數據:
1.在<進程線程.形象的說明進程和線程的區別?>說有些房間能夠容納n我的,好比廚房,也就是說,若是人數大於n,多出來的人只能在外面等着,這比如某些內存區域,只能供給固定數目的線程使用
2.在<進程線程.形象的說明進程和線程的區別?>說這時的解決辦法,就是在門口掛n把鎖,進去的人就取一把鑰匙,出來的就把鑰匙掛回原處,後來的人發現鑰匙架爲空,就知道必須門前排隊等着,這種作法叫"信號量",用來保證多個線程不會相互衝突
#!/usr/bin/env python # -*- coding: utf-8 -*- """ # # Authors: limanman # OsChina: http://my.oschina.net/pydevops/ # Purpose: # """ import time import pprint import threading class MultThread(threading.Thread): def __init__(self, thread_semaphore): super(MultThread, self).__init__() self.thread_semaphore = thread_semaphore def run(self): # 獲取鎖 self.thread_semaphore.acquire() # 等待1秒,開始第二波 print 'run the thread %s' % (self.name) time.sleep(2) # 釋放鎖 self.thread_semaphore.release() if __name__ == '__main__': # 建立一個信號量對象,只容許同時5個線程運行,其餘的線程只有其中有線程結束才能運行 semaphore = threading.BoundedSemaphore(5) # 建立100個線程,可是每次只容許5個線程同時容許 for _ in xrange(10): MultThread(semaphore).start()