1. 程式人生 > >Python多執行緒與多程序程式設計(一) 就這麼簡單

Python多執行緒與多程序程式設計(一) 就這麼簡單

"""

<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結果(順序與提交相同)

 

多程序程式設計>>>見下篇