日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > python >内容正文

python

python中多进程+协程的使用以及为什么要用它

發(fā)布時(shí)間:2025/3/21 python 18 豆豆
生活随笔 收集整理的這篇文章主要介紹了 python中多进程+协程的使用以及为什么要用它 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

前面講了為什么python里推薦用多進(jìn)程而不是多線程,但是多進(jìn)程也有其自己的限制:相比線程更加笨重、切換耗時(shí)更長(zhǎng),并且在python的多進(jìn)程下,進(jìn)程數(shù)量不推薦超過CPU核心數(shù)(一個(gè)進(jìn)程只有一個(gè)GIL,所以一個(gè)進(jìn)程只能跑滿一個(gè)CPU),因?yàn)橐粋€(gè)進(jìn)程占用一個(gè)CPU時(shí)能充分利用機(jī)器的性能,但是進(jìn)程多了就會(huì)出現(xiàn)頻繁的進(jìn)程切換,反而得不償失。

不過特殊情況(特指IO密集型任務(wù))下,多線程是比多進(jìn)程好用的。

舉個(gè)例子:給你200W條url,需要你把每個(gè)url對(duì)應(yīng)的頁面抓取保存起來,這種時(shí)候,單單使用多進(jìn)程,效果肯定是很差的。為什么呢?

例如每次請(qǐng)求的等待時(shí)間是2秒,那么如下(忽略cpu計(jì)算時(shí)間):

1、單進(jìn)程+單線程:需要2秒*200W=400W秒==1111.11個(gè)小時(shí)==46.3天,這個(gè)速度明顯是不能接受的

2、單進(jìn)程+多線程:例如我們?cè)谶@個(gè)進(jìn)程中開了10個(gè)多線程,比1中能夠提升10倍速度,也就是大約4.63天能夠完成200W條抓取,請(qǐng)注意,這里的實(shí)際執(zhí)行是:線程1遇見了阻塞,CPU切換到線程2去執(zhí)行,遇見阻塞又切換到線程3等等,10個(gè)線程都阻塞后,這個(gè)進(jìn)程就阻塞了,而直到某個(gè)線程阻塞完成后,這個(gè)進(jìn)程才能繼續(xù)執(zhí)行,所以速度上提升大約能到10倍(這里忽略了線程切換帶來的開銷,實(shí)際上的提升應(yīng)該是不能達(dá)到10倍的),但是需要考慮的是線程的切換也是有開銷的,所以不能無限的啟動(dòng)多線程(開200W個(gè)線程肯定是不靠譜的)

3、多進(jìn)程+多線程:這里就厲害了,一般來說也有很多人用這個(gè)方法,多進(jìn)程下,每個(gè)進(jìn)程都能占一個(gè)cpu,而多線程從一定程度上繞過了阻塞的等待,所以比單進(jìn)程下的多線程又更好使了,例如我們開10個(gè)進(jìn)程,每個(gè)進(jìn)程里開20W個(gè)線程,執(zhí)行的速度理論上是比單進(jìn)程開200W個(gè)線程快10倍以上的(為什么是10倍以上而不是10倍,主要是cpu切換200W個(gè)線程的消耗肯定比切換20W個(gè)進(jìn)程大得多,考慮到這部分開銷,所以是10倍以上)。

還有更好的方法嗎?答案是肯定的,它就是:

4、協(xié)程,使用它之前我們先講講what/why/how(它是什么/為什么用它/怎么使用它)

what:

協(xié)程是一種用戶級(jí)的輕量級(jí)線程。協(xié)程擁有自己的寄存器上下文和棧。協(xié)程調(diào)度切換時(shí),將寄存器上下文和棧保存到其他地方,在切回來的時(shí)候,恢復(fù)先前保存的寄存器上下文和棧。因此:

協(xié)程能保留上一次調(diào)用時(shí)的狀態(tài)(即所有局部狀態(tài)的一個(gè)特定組合),每次過程重入時(shí),就相當(dāng)于進(jìn)入上一次調(diào)用的狀態(tài),換種說法:進(jìn)入上一次離開時(shí)所處邏輯流的位置。

在并發(fā)編程中,協(xié)程與線程類似,每個(gè)協(xié)程表示一個(gè)執(zhí)行單元,有自己的本地?cái)?shù)據(jù),與其它協(xié)程共享全局?jǐn)?shù)據(jù)和其它資源。

why:

目前主流語言基本上都選擇了多線程作為并發(fā)設(shè)施,與線程相關(guān)的概念是搶占式多任務(wù)(Preemptive multitasking),而與協(xié)程相關(guān)的是協(xié)作式多任務(wù)。

不管是進(jìn)程還是線程,每次阻塞、切換都需要陷入系統(tǒng)調(diào)用(system call),先讓CPU跑操作系統(tǒng)的調(diào)度程序,然后再由調(diào)度程序決定該跑哪一個(gè)進(jìn)程(線程)。
而且由于搶占式調(diào)度執(zhí)行順序無法確定的特點(diǎn),使用線程時(shí)需要非常小心地處理同步問題,而協(xié)程完全不存在這個(gè)問題(事件驅(qū)動(dòng)和異步程序也有同樣的優(yōu)點(diǎn))。

因?yàn)閰f(xié)程是用戶自己來編寫調(diào)度邏輯的,對(duì)CPU來說,協(xié)程其實(shí)是單線程,所以CPU不用去考慮怎么調(diào)度、切換上下文,這就省去了CPU的切換開銷,所以協(xié)程在一定程度上又好于多線程。

how:

python里面怎么使用協(xié)程?答案是使用gevent,使用方法:看這里

使用協(xié)程,可以不受線程開銷的限制,我嘗試過一次把20W條url放在單進(jìn)程的協(xié)程里執(zhí)行,完全沒問題。

所以最推薦的方法,是多進(jìn)程+協(xié)程(可以看作是每個(gè)進(jìn)程里都是單線程,而這個(gè)單線程是協(xié)程化的)

多進(jìn)程+協(xié)程下,避開了CPU切換的開銷,又能把多個(gè)CPU充分利用起來,這種方式對(duì)于數(shù)據(jù)量較大的爬蟲還有文件讀寫之類的效率提升是巨大的。

?

小例子:

#-*- coding=utf-8 -*-
import requests
from multiprocessing import Process
import gevent
from gevent import monkey; monkey.patch_all()

import sys
reload(sys)
sys.setdefaultencoding('utf8')
def fetch(url):
try:
s = requests.Session()
r = s.get(url,timeout=1)#在這里抓取頁面
except Exception,e:
print e
return ''

def process_start(url_list):
tasks = []
for url in url_list:
tasks.append(gevent.spawn(fetch,url))
gevent.joinall(tasks)#使用協(xié)程來執(zhí)行

def task_start(filepath,flag = 100000):#每10W條url啟動(dòng)一個(gè)進(jìn)程
with open(filepath,'r') as reader:#從給定的文件中讀取url
url = reader.readline().strip()
url_list = []#這個(gè)list用于存放協(xié)程任務(wù)
i = 0 #計(jì)數(shù)器,記錄添加了多少個(gè)url到協(xié)程隊(duì)列
while url!='':
i += 1
url_list.append(url)#每次讀取出url,將url添加到隊(duì)列
if i == flag:#一定數(shù)量的url就啟動(dòng)一個(gè)進(jìn)程并執(zhí)行
p = Process(target=process_start,args=(url_list,))
p.start()
url_list = [] #重置url隊(duì)列
i = 0 #重置計(jì)數(shù)器
url = reader.readline().strip()
if url_list not []:#若退出循環(huán)后任務(wù)隊(duì)列里還有url剩余
p = Process(target=process_start,args=(url_list,))#把剩余的url全都放到最后這個(gè)進(jìn)程來執(zhí)行
p.start()

if __name__ == '__main__':
task_start('./testData.txt')#讀取指定文件


細(xì)心的同學(xué)會(huì)發(fā)現(xiàn):上面的例子中隱藏了一個(gè)問題:進(jìn)程的數(shù)量會(huì)隨著url數(shù)量的增加而不斷增加,我們?cè)谶@里不使用進(jìn)程池multiprocessing.Pool來控制進(jìn)程數(shù)量的原因是multiprocessing.Pool和gevent有沖突不能同時(shí)使用,但是有興趣的同學(xué)可以研究一下gevent.pool這個(gè)協(xié)程池。
---------------------
作者:L瑜
來源:CSDN
原文:https://blog.csdn.net/lambert310/article/details/51162634
版權(quán)聲明:本文為博主原創(chuàng)文章,轉(zhuǎn)載請(qǐng)附上博文鏈接!

轉(zhuǎn)載于:https://www.cnblogs.com/ExMan/p/10426777.html

總結(jié)

以上是生活随笔為你收集整理的python中多进程+协程的使用以及为什么要用它的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。