Python 線程、線程通信、多線程

這是一篇學(xué)習(xí)Python 線程相關(guān)的內(nèi)容,記錄一下以備復(fù)習(xí)和開發(fā)使用,技術(shù)有限,如有問題歡迎指出,多謝。


一.GIL 全局解釋器鎖(cpython)

1.為什么會有這個鎖:為了線程安全,減少python使用者的上手難度
GIL 使得同一個時刻只有一個線程在一個cpu上執(zhí)行字節(jié)碼,無法隱射到多個cpu,多核上執(zhí)行。
2.特殊情況下會釋放GIL:達(dá)到特定字節(jié)碼行數(shù)、到底特定數(shù)目時間片、IO操作(主動)

二:并發(fā)和并行的區(qū)別
  • 并發(fā):描述程序的組織結(jié)構(gòu),指程序要被設(shè)計成多個可獨立執(zhí)行的子任務(wù)
  • 并行:描述程序的執(zhí)行狀態(tài),指多任務(wù)需要同時執(zhí)行
三:守護(hù)線程&線程阻塞
  • 守護(hù)線程:thread.setDaemon(true),當(dāng)主程序退出的時候讓子程序也一并退出
  • 子線程阻塞:thread.join(),當(dāng)子程序都結(jié)束后主程序再退出
四:多線程的寫法
  • 實例化Threading,調(diào)用Threading的方法去進(jìn)行多線程編程
  • 寫子類繼承Theading,重寫相應(yīng)的方法
    說明:當(dāng)程序簡單時可使用實例化方法,當(dāng)程序較復(fù)雜的時候,實現(xiàn)邏輯較多,第二種方法。
五:線程間通信
  • 1.共享變量:
    方法簡單,也可以寫入到單獨的py文件中。問題:線程不安全,易出問題。
  • 2.queue 隊列:
    使用queue 的 Queue,這個是線程安全的,多線程取數(shù)據(jù)不會出錯。
    內(nèi)部使用的是deque Python 的雙端隊列,在字節(jié)碼的層面上就已經(jīng)到達(dá)了線程安全。
q = Queue()
# 方法:
q.put()  # 放入數(shù)據(jù)
q.get()  # 取出數(shù)據(jù)
q.put_nowait()  # 放入數(shù)據(jù),不用等待它完成再返回,異步的方法
q.get_nowait()  # 取出數(shù)據(jù),不用等待它完成再返回,異步的方法

get() put(),可以設(shè)置是否阻塞的,默認(rèn)是阻塞的

q.join()方法:
只有q.task_done()調(diào)用了join()才會讓主線程退出,成對使用。

六:線程同步
  • Lock 鎖
lock= Theading.Lock()
# 獲取鎖:
lock.acquire()
lock.release()

# 另一種方法:
with lock:
    # do something

加鎖的代碼段同時只有這一個代碼段在執(zhí)行,方式數(shù)據(jù)出問題。
缺點:1.用鎖會影響性能 2. 可能引起死鎖
死鎖情況:1.有acquire 沒有release 2. 相互等待

  • RLock 可重入鎖
    當(dāng)在一個線程中多個地方需要加鎖的時候用Lock 是不行的,需要用到RLock ,但是要注意的是獲取和釋放鎖的數(shù)量要一致,成對出現(xiàn)。
  • Condition 條件變量
    用于復(fù)雜的線程間同步,是一個同步鎖。例如:先后順序的多線程通信。
    重點函數(shù):wait() notify()
con = theading.Condition()
with con:
    # do something
    cond.notify()   #通知其他線程
    cond.wait()    # 等待其他線程通知
    # do something

注意:
1.先con.acquire()或者with con,獲取condition鎖,不然wait() notify() 不能用
2.Condition 有兩把鎖:一把底層鎖會在線程調(diào)用了wait() 的時候釋放,上面的鎖會在每次調(diào)用wait的時候分配一把并放入condition的等待隊列中,等到notify()的喚醒

  • Semaphore 信號量
    用于控制某段代碼進(jìn)入線程的數(shù)量,比如控制爬蟲的并發(fā)量。
import threading
import time


class HtmlSppier(threading.Thread):
    def __init__(self, url, sem):
        super().__init__()
        self.sem = sem
        self.url = url

    def run(self):
        time.sleep(2)
        print('download html success')
        self.sem.release()

class UrlProducer(threading.Thread):
    def __init__(self,sem):
        super().__init__()
        self.sem = sem


    def run(self):
        for i in range(20):
            self.sem.acquire()
            html_thread = HtmlSppier(f'http://www.qq.com/pn={i}',self.sem)
            html_thread.start()


if __name__ == '__main__':
    sem = threading.Semaphore(3)
    url_produce = UrlProducer(sem)
    url_produce.start()
七:線程池

為什么要使用線程池?
主線程中可以獲取某一個線程的狀態(tài)或者某一個的任務(wù)的狀態(tài)以及返回值
當(dāng)一個線程完成的時候主線程能立即知道

import requests

def download_html(i):
    url = f'https://www.baidu.com/s?ie=UTF-8&wd={i}'
    response = requests.get(url).text
    print(response)

ids = list(range(100))


# 線程池方式一:
import threadpool
def thread_main(item):
    pool = threadpool.ThreadPool(30)
    tasks = threadpool.makeRequests(download_html, ids)
    [pool.putRequest(req) for req in tasks]
    pool.wait()


# 線程池方式二:
from multiprocessing.dummy import   Pool as thpool

def thread_pool(item):
    pool = thpool(20)
    pool.map(download_html, ids)
    pool.close()
    pool.join()


# 線程池方式三(推薦):
from concurrent.futures import ThreadPoolExecutor

with ThreadPoolExecutor(max_workers=8) as exe:
    exe.map(download_html,ids)

推薦使用 concurrent.futures 模塊,線程池和進(jìn)程池的接口很相似,方便使用。

ThreadPoolExecutor 其他方法使用:
# 其他接口使用:
from concurrent.futures import ThreadPoolExecutor, as_completed,wait


executor = ThreadPoolExecutor(max_workers=8)

# 通過 submit 提交執(zhí)行的函數(shù)到線程中
task1 = executor.submit(download_html, (1))
task2 = executor.submit(download_html, (3))

# done() 判斷 task 是否完成
print(task1.done())
time.sleep(4)
print(task1.done())

# result() 獲取 task 的執(zhí)行結(jié)果 阻塞
print(task1.result())

# cancel() 取消任務(wù),如果任務(wù)在執(zhí)行中或者執(zhí)行完了是不能取消的
# 現(xiàn)在線程池是8 兩個任務(wù)都會被提交任務(wù)去執(zhí)行,如果 max_workers = 1,執(zhí)行task2.cancel()就會成功取消
print(task2.cancel())


# as_completed() 獲取已經(jīng)成功的task的返回數(shù)據(jù),阻塞
# as_completed實際上是一個生成器,里面有 yield 會把已經(jīng)完成的 future (task) 返回結(jié)果
ids = list(range(10))
all_task = [executor.submit(download_html,(i)) for i in ids]
time.sleep(8)
# 這是異步的,誰完成就處理誰
for future in as_completed(all_task):
    data = future.result()
    print(f'html response {data}')


# 通過 executor 獲取已經(jīng)完成的task
for data in executor.map(download_html,ids):
    print(f'html response {data}')


# wait() 等待task完成
ids = list(range(10))
all_task = [executor.submit(download_html,(i)) for i in ids]

#  wait 的 return_when 可選項
FIRST_COMPLETED = 'FIRST_COMPLETED'
FIRST_EXCEPTION = 'FIRST_EXCEPTION'
ALL_COMPLETED = 'ALL_COMPLETED'
_AS_COMPLETED = '_AS_COMPLETED'

wait(all_task, return_when=ALL_COMPLETED)
八:總結(jié)

Python 多線程首選concurrent.futures 中的 ThreadPoolExecutor,使用簡單方便,而且切換多進(jìn)程也是很快速的,后面繼續(xù)記錄多進(jìn)程方面的知識點。
代碼位置:github.com/rieuse/learnPython

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,345評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,494評論 3 416
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,283評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,953評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,714評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,410評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,940評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,776評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,210評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,654評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內(nèi)容