溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

如何在python中使用線程池和進程池

發(fā)布時間:2021-05-12 17:02:49 來源:億速云 閱讀:212 作者:Leah 欄目:開發(fā)技術(shù)

今天就跟大家聊聊有關(guān)如何在python中使用線程池和進程池,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。

一、需求

最近準備爬取某電商網(wǎng)站的數(shù)據(jù),先不考慮代理、分布式,先說效率問題(當然你要是請求的太快就會被封掉,親測,400個請求過去,服務(wù)器直接拒絕連接,心碎),步入正題。一般情況下小白的我們第一個想到的是for循環(huán),這個可是單線程啊。那我們考慮for循環(huán)直接開他個5個線程,問題來了,如果有一個url請求還沒有回來,后面的就干等,這么用多線程等于沒用,到處貼創(chuàng)可貼。

二、性能考慮

確定要用多線程或者多進程了,那我們到底是用多線程還是多進程,有些人對多進程和多線程有一定的偏見,就因為python的GIL鎖,下面我們說一下這兩個東西的差別。

三、多線程:

一般情況下我們啟動一個.py文件,就等于啟動了一個進程,一個進程里面默認有一個線程工作,我們使用的多線程的意思就是在一個進程里面啟用多個線程。但問題來了,為什么要使用多線程呢?我知道啟動一個進程的時候需要創(chuàng)建一些內(nèi)存空間,就相當于一間房子,我們要在這個房子里面干活,你可以想一個人就等于一個線程,你房子里面有10個人的空間跟有20個人的空間,正常情況下是不一樣的,因為我們知道線程和線程之間默認是可以通信的(進程之間默認是不可以通信的,不過可以用技術(shù)實現(xiàn),比如說管道)。可以多線程為了保證計算數(shù)據(jù)的正確性,所以出現(xiàn)了GIL鎖,保證同一時間只能有一個線程在計算。GIL鎖你可以基本理解為,比如在這個房間里要算一筆賬,在同一時間內(nèi)只能有一個人在算這筆賬,想一個問題,如果這筆賬5個人就能算清楚,我需要10平米的房間就行,那為什么要請10個人,花20平米呢?所以并不是開的線程越多越好。但是,但是,但是,注意大家不用動腦筋(CPU計算)算這筆賬的時候可以去干別的事(比如說5個人分工,各算一部分),比如說各自把自己算完后的結(jié)果記錄在賬本上以便后面對賬,這個的話每個人都有自己的賬本,所以多線程適合IO操作,記住了就算是適合IO操作,也不代表說人越多越好,所以這個量還是得根據(jù)實際情況而定。

線程池示例:

import requests
from concurrent.futures import ThreadPoolExecutor
urls_list = [
  'https://www.baidu.com',
  'http://www.gaosiedu.com',
  'https://www.jd.com',
  'https://www.taobao.com',
  'https://news.baidu.com',
]
pool = ThreadPoolExecutor(3)
def request(url):
  response = requests.get(url)
  return response
def read_data(future,*args,**kwargs):
  response = future.result()
  response.encoding = 'utf-8'
  print(response.status_code,response.url)
def main():
  for url in urls_list:
    done = pool.submit(request,url)
    done.add_done_callback(read_data)
if __name__ == '__main__':
  main()
  pool.shutdown(wait=True)

四、多進程:

上面我們介紹了多線程(線程池),現(xiàn)在我們聊聊進程池,我們知道一個進程占用一個CPU,現(xiàn)在的配置CPU一般都是4核,我們啟動兩個進程就是分別在兩個CPU里面(兩個內(nèi)核)各運行一個進程,我知道進程里面才有線程,默認是一個。但是有個缺點,按照上面的說法,開兩個進程占用的內(nèi)存空間是開一個進程占用內(nèi)存空間的2倍。CPU就占用了2個核,電腦還得干別的事兒對吧,不能冒冒失失瞎用。開的太多是不是其他程序就得等著,我們思考一下,占用這么多的內(nèi)存空間,利用了多個CPU的優(yōu)點為了什么?CPU是用來做什么的?沒錯就是用來計算的,所以在CPU密集運算的情況下建議用多進程。注意,具體要開幾個進程,根據(jù)機器的實際配置和實際生產(chǎn)情況而定。

進程池

import requests
from concurrent.futures import ProcessPoolExecutor
urls_list = [
  'https://www.baidu.com',
  'http://www.gaosiedu.com',
  'https://www.jd.com',
  'https://www.taobao.com',
  'https://news.baidu.com',
]
pool = ProcessPoolExecutor(3)
def request(url):
  response = requests.get(url)
  return response
def read_data(future,*args,**kwargs):
  response = future.result()
  response.encoding = 'utf-8'
  print(response.status_code,response.url)
def main():
  for url in urls_list:
    done = pool.submit(request,url)
    done.add_done_callback(read_data)
if __name__ == '__main__':
  main()
  pool.shutdown(wait=True)

總結(jié):

1、多線程適合IO密集型程序

2、多進程適合CPU密集運算型程序

五、協(xié)程:

協(xié)程:又稱微線程纖程。英文名Coroutine。那協(xié)程到底是個什么東西,通俗的講就是比線程還要小的線程,所以才叫微線程。

主要作用:有人要問了,在python中線程是原子操作(意思就是說一句話或者一個動作就能搞定的操作或者計算),怎么還有個叫協(xié)程的呢?

優(yōu)點:

1、使用高并發(fā)、高擴展、低性能的;一個CPU支持上萬的協(xié)程都不是問題。所以很適合用于高并發(fā)處理。

2、無需線程的上下文切換開銷(乍一看,什么意思呢?我們都知道python實際上是就是單線程,那都是怎么實現(xiàn)高并發(fā)操作呢,就是CPU高速的切換,每個任務(wù)都干一點,最后看上去是一起完事兒的,肉眼感覺就是多線程、多進程)

缺點:

1、無法利用CPU的多核優(yōu)點,這個好理解,進程里面包含線程,而協(xié)程就是細分后的線程,也就是說一個進程里面首先是線程其后才是協(xié)程,那肯定是用不了多核了,不過可以多進程配合,使用CPU的密集運算,平時我們用不到。

一般情況下用的比較多的是asyncio或者是gevent這兩個技術(shù)實現(xiàn)協(xié)程,asyncio是python自帶的技術(shù),gevent第三方庫,個人比較喜歡gevent這個技術(shù)。

gevent:

安裝:gevent需要安裝greenlet,因為它是使用到了greenlet這個庫。

pip3 install greenlet
pip3 install gevent

1、gevent的基本實現(xiàn),按照下面的寫法,程序啟動后將會開啟許許多多的協(xié)程,反而特別影響性能。

gevent+requests:

import requests
import gevent
from gevent import monkey
#把當前的IO操作,打上標記,以便于gevent能檢測出來實現(xiàn)異步(否則還是串行)
monkey.patch_all()
def task(url):
  '''
  1、request發(fā)起請求
  :param url: 
  :return: 
  '''
  response = requests.get(url)
  print(response.status_code)
gevent.joinall([
  gevent.spawn(task,url='https://www.baidu.com'),
  gevent.spawn(task,url='http://www.sina.com.cn'),
  gevent.spawn(task,url='https://news.baidu.com'),
])

2、有一個改進版本,就是可以設(shè)置到底讓它一次發(fā)起多少個請求(被忘了,協(xié)程=高并發(fā)現(xiàn)實之一)。其實里面就是利用gevnet下的pool模塊里面的Pool控制每次請求的數(shù)量。

gevent+reqeust+Pool(控制每次請求數(shù)量)

import requests
import gevent
from gevent import monkey
from gevent.pool import Pool
#把當前的IO操作,打上標記,以便于gevent能檢測出來實現(xiàn)異步(否則還是串行)
monkey.patch_all()
def task(url):
  '''
  1、request發(fā)起請求
  :param url:
  :return:
  '''
  response = requests.get(url)
  print(response.status_code)
#控制最多一次向遠程提交多少個請求,None代表不限制
pool = Pool(5)
gevent.joinall([
  pool.spawn(task,url='https://www.baidu.com'),
  pool.spawn(task,url='http://www.sina.com.cn'),
  pool.spawn(task,url='https://news.baidu.com'),
])

3、還有一版本,每次我們都要裝greenlet和gevent這肯定是沒法子,但是,我們上面寫的這個改進版還是有點麻煩,所以就有人寫了100多行代碼把它們給搞到了一起,對就是搞到了一起,叫g(shù)requests,就是前者兩個技術(shù)的結(jié)合。

pip3 install grequests

這個版本是不是特別變態(tài),直接把requests、greenlet、gevent、Pool都省的導(dǎo)入了,但是裝還是要裝的,有人說從下面代碼中我沒看到Pool的參數(shù)啊,grequests.map(request_list,size=5),size就是你要同時開幾個協(xié)程,還有參數(shù)你得點進去看,是不是很牛,很輕松

grequests:

import grequests
request_list = [
  grequests.get('https://www.baidu.com'),
  grequests.get('http://www.sina.com.cn'),
  grequests.get('https://news.baidu.com'),
]
# ##### 執(zhí)行并獲取響應(yīng)列表 #####
response_list = grequests.map(request_list,size=5)
print(response_list)

結(jié)果返回一個列表,你可以再迭代一下就行了。

如何在python中使用線程池和進程池

python的五大特點是什么

python的五大特點:1.簡單易學,開發(fā)程序時,專注的是解決問題,而不是搞明白語言本身。2.面向?qū)ο?,與其他主要的語言如C++和Java相比, Python以一種非常強大又簡單的方式實現(xiàn)面向?qū)ο缶幊獭?.可移植性,Python程序無需修改就可以在各種平臺上運行。4.解釋性,Python語言寫的程序不需要編譯成二進制代碼,可以直接從源代碼運行程序。5.開源,Python是 FLOSS(自由/開放源碼軟件)之一。

看完上述內(nèi)容,你們對如何在python中使用線程池和進程池有進一步的了解嗎?如果還想了解更多知識或者相關(guān)內(nèi)容,請關(guān)注億速云行業(yè)資訊頻道,感謝大家的支持。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI