"""
<axiner>聲明:
(錯了另刂扌丁我)
(如若有誤,請記得指出喲,謝謝了!!!)
"""
先來了解一個概念,GIL? GIL的全稱爲Global Interpreter Lock, 全局解釋器鎖。
Python代碼的執行由Python 虛擬機(也叫解釋器主循環,CPython版本)來控制,Python 在設計之初就考慮到要在解釋器的主循環中,同時只有一個線程在執行,即在任意時刻,只有一個線程在解釋器中運行。對Python 虛擬機的訪問由全局解釋器鎖(GIL)來控制,正是這個鎖能保證同一時刻只有一個線程在運行。
也就是說並沒有正真的多線程.....
爲什麼又有多線程編程呢?
GIL鎖的釋放,並不是GIL鎖的獲取者會一條路走到黑,也就是說在執行的某些中途會釋放GIL鎖,此時其它就有機會獲得GIL鎖了.....
Python內部算法機制有幾種釋放GIL鎖的方式:
1\ 時間片
2\ 字節碼長度
3\ io操作時
多線程的實現方式:
from threading import Thread
1、參數傳入
thread = Thread()
thread(target=func, args=())
2、類的繼承
class MyThread(Thread):
def __init__(self):
pass
def run(self):
# 重寫類下run()方法
pass
thread = Thread()
thread.SetDeamon(True) # 設置爲保護線程(即主線程退出其也退出)
thread.join() # 將線程阻塞直至線程完成
---------
線程間通信:
1、全局共享變量。資源競爭,不安全
2、queue。安全(from queue import Queue)
注意:
queue.join() # 阻塞作用。要退出,則在其前加上queue.task_done()<成對出現>
---------
線程間同步:
1、Lock, RLook
使用:在需要的代碼塊加上鎖,acquire獲取鎖-release釋放鎖
鎖的缺點:
1、鎖會影響性能;
2、鎖會引起死鎖。
引起死鎖原因:a.多次acquire而不釋放; b.互相等待對方造成資源競爭; c.lock中的子函數也有lock(此應用RLock)
2、Condition(條件變量)
from threading import Condition
例如:A與B對話(A先說)
from threading import Thread, Condition
class A(Thread):
def __init__(self, cond):
super().__init__(name="A")
self.conf = cond
def __run__(self):
with self.cond: # 獲取condition
print("{}: hello!".format(self.name))
self.cond.notify() # 通知等待(wait)
self.cond.wait() # 阻塞等待通知(notify)
class B(Thread):
def __init__(self, cond):
super().__init__(name="B")
self.conf = cond
def __run__(self):
with self.cond: # 獲取condition
self.cond.wait()
print("{}: 你好!".format(self.name))
self.cond.notify()
self.cond.wait()
3、Semaphore(信號)
# 是用於控制進入數量的鎖(文件的讀寫,寫一般只用一個線程,讀可以允許有多個線程)
例如:爬取url及解析頁面
from threading import Thread, Semaphore
class Html_Spider(Thread):
def __init__(self, sem):
super().__init__(name="B")
self.sem = sem
def __run__(self):
time.sleep(2) # 模擬
print("success...")
self.sem.release() # 完成後釋放
class UrlProducter(Thread):
def __init__(self, sem):
super().__init__(name="B")
self.sem = sem
def __run__(self):
for i in range(20):
self.acquire() # 獲取
html_thread = Html_Spider("http://www.foo/{0}".format(i), self.sem)
html_thread.start()
if __name__ == "__main__":
sem = Semaphore(3) # 鎖的數量
url_producter = UrlProducter(sem)
url_producter.start()
---------
關於線程池
爲什麼用線程池?
1、控制線程數量併發(semaphore也有這功能)
2、可線程狀態及返回值
3、當一個線程完成時,主線程可以立即知道
(4、futures可以讓多線程和多進程接口一致)
from concurrent import futures
eg:
# 模擬html獲取
import time
from concurrent.futures import ThreadPoolExecutor
def get_html(t):
time.sleep(2)
print("get page success: {0}".format(t))
return t
executor = ThreadPoolExecutor(max_work=2)
1\\ 提交任務
# 1-窮舉提交
task1 = executor.submit(get_html, (2,))
task2 = executor.submit(get_html, (4,))
# 2-批量提交
ts = [2, 4]
all_task = [executor.submit(get_html, t) for t in ts]
# 總結
executor.submit() # 將執行函數提交到線程池中,非阻塞立即返回
另:
executor.done() # 判斷某任務是否完成
executor.result() # 獲取結果,阻塞式
executor.cancel() # 未提交的任務可取消
from concurrent.futures import wait
wait() # 等待xx完成,才執行主線程
wait有timeout和return_when兩個參數可以設置。
timeout控制wait()方法返回前等待的時間。
return_when決定方法什麼時間點返回:如果採用默認的ALL_COMPLETED,程序會阻塞直到線程池裏面的所有任務都完成;如果採用FIRST_COMPLETED參數,程序並不會等到線程池裏面所有的任務都完成。
2\\ 獲取結果
from concurrent.futures import as_completed
# 一
for future in as_completed(all_task):
data = future.result()
print("get success page: {0}".format(data))
------------
另:提交任務+獲取結果
for data in executor.map(get_html, ts):
print(data)
# as_completed 與 executor.map
as_completed是concurrent.futures的函數,返回futures對象(順序與執行相同)
map是futures.ThreadPoolExecutor下的方法,返回data結果(順序與提交相同)
多進程編程>>>見下篇