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

 

多進程編程>>>見下篇

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章