国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

這篇博客和你嘮嘮 python 并發(fā),滾雪球?qū)Wpython第四季,第16篇

qpwoeiru96 / 1054人閱讀

摘要:圖片下載屬于操作,比較耗時(shí),基于此,可以利用中的多線程將其實(shí)現(xiàn)。更多精彩滾雪球?qū)W完結(jié)滾雪球?qū)W第二輪完結(jié)滾雪球?qū)W第三輪滾雪球?qū)W番外篇完結(jié)

在 python 編碼過(guò)程中,有時(shí)存在這樣的一個(gè)需求,同時(shí)下載 N 張圖片,并且要快。

一般這樣的需求,只需要編寫(xiě)一個(gè) for 循環(huán)即可實(shí)現(xiàn),但是加上 這個(gè)要求,就不好實(shí)現(xiàn)了。

圖片下載屬于 I/O 操作,比較耗時(shí),基于此,可以利用 python 中的多線程將其實(shí)現(xiàn)。

為了不讓大家學(xué)的太困倦,特意找來(lái) 6 張美麗的圖片,本次學(xué)習(xí)將圍繞這幾張圖片進(jìn)行。

https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv.jpghttps://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-004.jpghttps://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-012.jpghttps://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-013.jpghttps://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-016.jpghttps://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-010.jpg

單線程下載 6 張圖片

使用 for 循環(huán),同步代碼如下所示:

import timeimport requestsurls = [    "https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv.jpg",    "https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-004.jpg",    "https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-012.jpg",    "https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-013.jpg",    "https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-016.jpg",    "https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-010.jpg"]# 文件保存路徑SAVE_DIR = "./928/"def save_img(url):    res = requests.get(url)    with open(F"{SAVE_DIR}{time.time()}.jpg", "wb+") as f:        f.write(res.content)if __name__ == "__main__":    # 下載開(kāi)始時(shí)間    start_time = time.perf_counter()    for url in urls:        save_img(url)    print("下載 6 張圖片消耗時(shí)間為:", time.perf_counter() - start_time)    # 下載 6 張圖片消耗時(shí)間為: 1.911142665

concurrent.futures 模塊下載 6 張圖片

接下來(lái)使用 concurrent.futures 模塊 實(shí)現(xiàn)對(duì) 6 張圖片的下載,這個(gè)模塊實(shí)現(xiàn)了 ThreadPoolExecutor 類(lèi)和 ProcessPoolExecutor 類(lèi),都繼承自Executor,分別被用來(lái)創(chuàng)建 線程池進(jìn)程池,接受 max_workers 參數(shù),代表創(chuàng)建的線程數(shù)或者進(jìn)程數(shù)。

這兩個(gè)類(lèi)可以在不同的線程或進(jìn)程中執(zhí)行 可調(diào)用對(duì)象ProcessPoolExecutormax_workers 參數(shù)可以為空,程序會(huì)自動(dòng)創(chuàng)建與電腦 CPU數(shù)目相同的進(jìn)程數(shù)。

使用 ThreadPoolExecutor 實(shí)現(xiàn)多線程下載

import timeimport requestsfrom concurrent import futuresMAX_WORKERS = 20  # 最大線程數(shù)SAVE_DIR = "./928/"  # 文件保存路徑urls = [    "https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv.jpg",    "https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-004.jpg",    "https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-012.jpg",    "https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-013.jpg",    "https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-016.jpg",    "https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-010.jpg"]def save_img(url):    res = requests.get(url)    with open(F"{SAVE_DIR}{time.time()}.jpg", "wb+") as f:        f.write(res.content)if __name__ == "__main__":    start_time = time.perf_counter()  # 下載開(kāi)始時(shí)間    with futures.ThreadPoolExecutor(MAX_WORKERS) as executor:        res = executor.map(save_img, urls) # executor.map() 方法會(huì)返回一個(gè)生成器,后續(xù)代碼可以迭代獲取每個(gè)線程的執(zhí)行結(jié)果    print("下載 6 張圖片消耗時(shí)間為:", time.perf_counter() - start_time)    # 下載 6 張圖片消耗時(shí)間為: 0.415939759

當(dāng)使用多線程代碼之后,時(shí)間從單線程的 1.9s 變?yōu)榱硕嗑€程的 0.4s,能看到效率的提升。

Future 類(lèi)

在上述多線程代碼中,使用了 concurrent 庫(kù)中的 future 對(duì)象,該對(duì)象是 Future 類(lèi)的對(duì)象,它的實(shí)力表示可能已經(jīng)完成尚未完成的 延遲計(jì)算,該類(lèi)具備 done() 方法,返回調(diào)用對(duì)象是否已經(jīng)執(zhí)行,有該方法的同時(shí)還具備一個(gè) add_done_callback() 方法,表示調(diào)用對(duì)象執(zhí)行完畢的回調(diào)函數(shù)。

from concurrent.futures import ThreadPoolExecutordef print_name():    return "橡皮擦"def say_hello(obj):    """可調(diào)用對(duì)象執(zhí)行完畢,綁定的回調(diào)函數(shù)"""    w_name = obj.result()    s = w_name + "你好"    print(s)    return swith ThreadPoolExecutor(1) as executor:    executor.submit(print_name).add_done_callback(say_hello)

在上述代碼中用到了如下知識(shí)點(diǎn):

  • executor.map():該方法類(lèi)似 map 函數(shù),原型為 map(func, *iterables, timeout=None, chunksize=1),異步執(zhí)行 func,并支持多次并發(fā)調(diào)用;
  • executor.submit():方法原型為 submit(fn, *args, **kwargs),安排可調(diào)用對(duì)象 fnfn(*args, **kwargs) 的形式執(zhí)行,并返回 Future 對(duì)象來(lái)表示它的執(zhí)行結(jié)果,該方法只能進(jìn)行單個(gè)任務(wù),如果需要并發(fā)多個(gè)任務(wù),需要使用 map 或者 as_completed
  • future對(duì)象.result():返回調(diào)用返回的值,有個(gè)等待時(shí)間參數(shù) timeout 可以設(shè)置;
  • future對(duì)象add_done_callback():該方法中綁定的回調(diào)函數(shù)在 future 取消或者完成后運(yùn)行,表示 future 本身

補(bǔ)充說(shuō)明

as_completed() 方法
該方法參數(shù)是一個(gè) Future 列表,返回值是一個(gè) Future 組成的生成器,在調(diào)用 as_completed() 方法時(shí)不會(huì)阻塞,只有當(dāng)對(duì)迭代器進(jìn)行循環(huán)時(shí),每調(diào)用一次 next() 方法,如果當(dāng)前 Future 對(duì)象還未完成,則會(huì)阻塞。

修改下載 6 張圖片的代碼,使用 as_completed() 進(jìn)行實(shí)現(xiàn),最后得到的時(shí)間優(yōu)于單線程,但如果對(duì)結(jié)果進(jìn)行迭代,調(diào)用 result() 方法,則時(shí)間會(huì)加長(zhǎng)。

import timeimport requestsfrom concurrent.futures import ThreadPoolExecutor, as_completedMAX_WORKERS = 20  # 最大線程數(shù)SAVE_DIR = "./928/"  # 文件保存路徑urls = [    "https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv.jpg",    "https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-004.jpg",    "https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-012.jpg",    "https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-013.jpg",    "https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-016.jpg",    "https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-010.jpg"]def save_img(url):    res = requests.get(url)    with open(F"{SAVE_DIR}{time.time()}.jpg", "wb+") as f:        f.write(res.content)if __name__ == "__main__":    start_time = time.perf_counter()  # 下載開(kāi)始時(shí)間    with ThreadPoolExecutor(MAX_WORKERS) as executor:        tasks = [executor.submit(save_img, url) for url in urls]        # 去除下部分代碼,時(shí)間基本與 map 一致。        for future in as_completed(tasks):            print(future.result())    print("下載 6 張圖片消耗時(shí)間為:", time.perf_counter() - start_time)    # 下載 6 張圖片消耗時(shí)間為: 0.840261401

wait 方法
wait 方法可以讓主線程阻塞,直到滿足設(shè)定的要求,該要求為 return_when 參數(shù),其值有 ALL_COMPLETEDFIRST_COMPLETEDFIRST_EXCEPTION

import timeimport requestsfrom concurrent.futures import ThreadPoolExecutor, as_completed, wait, ALL_COMPLETED, FIRST_COMPLETEDMAX_WORKERS = 20  # 最大線程數(shù)SAVE_DIR = "./928/"  # 文件保存路徑urls = [    "https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv.jpg",    "https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-004.jpg",    "https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-012.jpg",    "https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-013.jpg",    "https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-016.jpg",    "https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-010.jpg"]def save_img(url):    res = requests.get(url)    with open(F"{SAVE_DIR}{time.time()}.jpg", "wb+") as f:        f.write(res.content)if __name__ == "__main__":    start_time = time.perf_counter()  # 下載開(kāi)始時(shí)間    with ThreadPoolExecutor(MAX_WORKERS) as executor:        tasks = [executor.submit(save_img, url) for url in urls]        wait(tasks, return_when=ALL_COMPLETED)        print("程序運(yùn)行完畢")    print("下載 6 張圖片消耗時(shí)間為:", time.perf_counter() - start_time)    # 下載 6 張圖片消耗時(shí)間為: 0.48876672

最后一句:ProcessPoolExecutor 的用法與 ThreadPoolExecutor 的用法基本一致,所以可以互通。

寫(xiě)在后面

以上內(nèi)容就是本文的全部?jī)?nèi)容,希望對(duì)學(xué)習(xí)路上的你有所幫助~

今天是持續(xù)寫(xiě)作的第 235 / 365 天。
期待 關(guān)注點(diǎn)贊評(píng)論收藏

更多精彩

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/122205.html

相關(guān)文章

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<