python中如果是 i/o密集型的操作,用多執行緒(協程asyncio、執行緒threading),如果i/o操作很慢,需要很多任務/執行緒協同操作,用asyncio,如果需要有限數量的任務/執行緒,那麼使用多執行緒。
如果是cpu密集型操作,用多程序(multeprocessing)。
設計者為了規避類似記憶體管理這樣的複雜競爭風險問題(race condition);
cpython大量使用c語言庫,但大部分c語言庫都不是執行緒安全的(執行緒安全會降低效能和增加複雜度)。
繞過cpython,使用jpython等別的實現;
把關鍵效能**放到其他語言中實現,比如c++
asyncio包含有協程需要的所有魔法asyncio 和其他 python 程式一樣,是單執行緒的,它只有乙個主線程event loop,但是可以進行多個不同的任務(task),這裡的任務,就是特殊的 future 物件。這些不同的任務,被乙個叫做 event loop 的物件所控制。
當在 jupyter 中執行: %time asyncio.run(main([『url_1』, 『url_2』, 『url_3』,『url_4』]))
出現報錯: runtimeerror: asyncio.run() cannot be called from a running event loop原因是: the problem in your case is that jupyter (ipython)
is already running an event loop (for ipython ≥ 7.0) 解決是: 將 %time
asyncio.run(main([『url_1』, 『url_2』, 『url_3』, 『url_4』])) 換成 await
main([『url_1』, 『url_2』, 『url_3』, 『url_4』])
# 協程
import asyncio
async
defcrawl_page
(url)
:print
('crawling {}'
.format
(url)
) sleep_time =
int(url.split(
'_')[-
1])await asyncio.sleep(sleep_time)
print
('ok {}'
.format
(url)
)async
defmain
(urls)
: tasks =
[asyncio.create_task(crawl_page(url)
)for url in urls]
for task in tasks:
await task
# %time asyncio.run(main(['url_1', 'url_2', 'url_3', 'url_4']))
await main(
['url_1'
,'url_2'
,'url_3'
,'url_4'
])
結果如下圖:
threadingthreadingpool
# 非同步程式設計
from threading import thread
import time
import functools
# 裝飾器實現耗時記錄
deflog_execution_time
(func)
: @functools.wraps(func)
def(
*args,
**kwargs)
: start = time.perf_counter(
) res = func(
*args,
**kwargs)
end = time.perf_counter(
)print
('{} took {} s'
.format
(func.__name__,
(end - start)))
return res
@log_execution_time
defcountdown
(n):
while n >0:
n -=
1n =
100000000
t1 = thread(target=countdown, args=
[n //2]
)t2 = thread(target=countdown, args=
[n //2]
)t1.start(
)t2.start(
)t1.join(
)t2.join(
)
等待執行結束並返回結果
import concurrent.futures
import requests
import time
defdownload_one
(url)
: resp = requests.get(url)
# print('read {} from {}'.format(len(resp.content), url))
return
('read {} from {}'
.format
(len
(resp.content)
, url)
)def
download_all
(sites)
:with concurrent.futures.threadpoolexecutor(max_workers=5)
as executor:
# executor.map(download_one,sites)
to_do =
for site in sites:
future = executor.submit(download_one, site)
for future in to_do:
future.add_done_callback(
lambda future:
print
(future.result())
)def
main()
: sites =
['',''
,'',''
,'',''
,'',''
] start_time = time.perf_counter(
) download_all(sites)
end_time = time.perf_counter(
)print
('download {} sites in {} seconds'
.format
(len
(sites)
, end_time - start_time)
)if __name__ ==
'__main__'
: main(
)
import multiprocessing
import time
defcpu_bound
(number)
:print
(number)
return
sum(i * i for i in
range
(number)
)def
find_sums
(numbers)
:with multiprocessing.pool(
)as pool:
pool.
map(cpu_bound, numbers)
if __name__ ==
"__main__"
: numbers =
[1000000
+ x for x in
range(20
)]start_time = time.time(
) find_sums(numbers)
duration = time.time(
)- start_time
print
(f"duration seconds"
)
GIL 全域性直譯器鎖
1 描述python中gil的概念,以及它對python多執行緒的影響?編寫乙個多執行緒抓取網頁的程式,並闡明多執行緒抓取程式是否可比單執行緒效能有提公升,並解釋原因。答 gil global interpreter lock,即全域性直譯器鎖 1 python語言和gil沒有半毛錢關係。僅僅是由於...
GIL全域性直譯器鎖
gil 啥?他是如何產生的?gil產生的背景 在cpython解釋內部執行多個執行緒的時候,每個執行緒都需要直譯器內部申請相應的全域性資源,由於c語言本身比較底層造成cpython在管理所有全域性資源的時候並不能應對所有執行緒同時的資源請求,因此為了防止資源競爭而發生錯誤,對所有執行緒申請全域性資源...
全域性直譯器鎖GIL
我們使用高併發,一次是建立1萬個執行緒去修改乙個數並列印結果看現象 from threading import thread import osdef func args global n n args print n,os.getpid n 100t list for i in range 100...