溫馨提示×

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

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

Python多線程爬蟲舉例分析

發(fā)布時(shí)間:2021-11-23 14:22:12 來源:億速云 閱讀:197 作者:iii 欄目:大數(shù)據(jù)

這篇文章主要介紹“Python多線程爬蟲舉例分析”,在日常操作中,相信很多人在Python多線程爬蟲舉例分析問題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”Python多線程爬蟲舉例分析”的疑惑有所幫助!接下來,請(qǐng)跟著小編一起來學(xué)習(xí)吧!

線程和進(jìn)程如何工作

當(dāng)程序在運(yùn)行時(shí),就會(huì)創(chuàng)建包含代碼和狀態(tài)的進(jìn)程。這些進(jìn)程通過一個(gè)或者多個(gè)CPU來執(zhí)行。不過同一時(shí)刻每個(gè)CPU只會(huì)執(zhí)行一個(gè)進(jìn)程,然后在不同進(jìn)程之間快速切換,這樣就感覺多個(gè)程序同時(shí)運(yùn)行。同理,在一個(gè)進(jìn)程中,程序的執(zhí)行也是在不同線程間進(jìn)行切換的,每個(gè)線程執(zhí)行程序的不同部分。這就意味著一個(gè)線程在等待執(zhí)行時(shí),進(jìn)程會(huì)切換到其他的線程執(zhí)行,這樣可以避免浪費(fèi)CPU時(shí)間。

Threading線程模塊

在Python標(biāo)準(zhǔn)庫中,使用threading模塊來支持多線程。Threading模塊對(duì)thread進(jìn)行了封裝,絕大數(shù)情況,只需要使用threading這個(gè)模塊。使用起來也非常簡(jiǎn)單:

t1=threading.Thread(target=run,args=("t1",)) 創(chuàng)建一個(gè)線程實(shí)例
# target是要執(zhí)行的函數(shù)名(不是函數(shù)),args是函數(shù)對(duì)應(yīng)的參數(shù),以元組的形式存在
t1.start() 啟動(dòng)這個(gè)線程實(shí)例。

普通創(chuàng)建方式

線程的創(chuàng)建很簡(jiǎn)單,如下:

import threading
import time

def printStr(name):
    print(name+"-python青燈")
    s=0.5
    time.sleep(s)
print(name+"-python青燈")

t1=threading.Thread(target=printStr,args=("你好!",))
t2=threading.Thread(target=printStr,args=("歡迎你!",))
t1.start()
t2.start()

自定義線程

本質(zhì)是繼承threading.Thread,重構(gòu)Thread類中的run方法

import threading
import time

class testThread(threading.Thread):
    def __init__(self,s):
        super(testThread,self).__init__()
        self.s=s

    def run(self):
        print(self.s+"——python")
        time.sleep(0.5)
        print(self.s+"——青燈")

if __name__=='__main__':
    t1=testThread("測(cè)試1")
    t2=testThread("測(cè)試2")
    t1.start()
    t2.start()

守護(hù)線程

使用setDaemon(True)把子線程都變成主線程的守護(hù)線程,因此當(dāng)主線程結(jié)束后,子線程也會(huì)隨之結(jié)束。也就是說,主線程不等待其守護(hù)線程執(zhí)行完成再去關(guān)閉。

import threading
import time

def run(s):
    print(s,"python")
    time.sleep(0.5)
    print(s,"青燈")

if __name__ == "__main__":
    t=threading.Thread(target=run,args=("你好!",))
    t.setDaemon(True)
    t.start()
    print("end")

結(jié)果:

你好! python

end

當(dāng)主線程結(jié)束后,守護(hù)線程不管有沒有結(jié)束,都自動(dòng)結(jié)束。

主線程等待子線程結(jié)束

使用join方法,讓主線程等待子線程執(zhí)行。如下:

import threading
import time

def run(s):
    print(s,"python")
    time.sleep(0.5)
    print(s,"青燈")

if __name__ == "__main__":
    t=threading.Thread(target=run,args=("你好!",))
    t.setDaemon(True)
    t.start()
    t.join()
    print("end")

結(jié)果:

你好! python

你好! 青燈

end

以上是多線程的幾種簡(jiǎn)單的用法,那么threading模塊還有做什么呢?請(qǐng)往下看。

Lock 鎖

其實(shí)在介紹diskcache緩存的時(shí)候也介紹過鎖的相關(guān)內(nèi)容,其實(shí)不難理解為啥多線程中也會(huì)出現(xiàn)鎖的概念,當(dāng)沒有保護(hù)共享資源時(shí),多個(gè)線程在處理同一資源時(shí),可能會(huì)出現(xiàn)臟數(shù)據(jù),造成不可以預(yù)期的結(jié)果,即線程不安全。

如下示例出現(xiàn)不可預(yù)期的結(jié)果:

import threading

price=0
def changePrice(n):
    global price
    price=price+n
    price=price-n

def runChange(n):
    for i in range(2000000):
        changePrice(n)

if __name__ == "__main__":
    t1=threading.Thread(target=runChange,args=(5,))
    t2=threading.Thread(target=runChange,args=(8,))
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print(price)

理論上的結(jié)果為0,但是每次運(yùn)行的結(jié)果可能都是不一樣的。

所以這個(gè)時(shí)候就需要鎖去處理了,如下:

import threading
import time
from threading import Lock

price=0
def changePrice(n):   
    global price
    lock.acquire() #獲取鎖
    price=price+n
    print("price:"+str(price))
    price=price-n
    lock.release() #釋放鎖

def runChange(n):
    for i in range(2000000):
        changePrice(n)

if __name__ == "__main__":
    lock=Lock()
    t1=threading.Thread(target=runChange,args=(5,))
    t2=threading.Thread(target=runChange,args=(8,))
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print(price)

結(jié)果值與理論值是一致的。鎖的意義在于每次只允許一個(gè)線程去修改同一數(shù)據(jù),以保證線程安全。

信號(hào)量

BoundedSemaphore類,同時(shí)允許一定數(shù)量的線程更改數(shù)據(jù),如下:

import threading
import time

def work(n):
    semaphore.acquire()
    print("序號(hào):"+str(n))
    time.sleep(1)
    semaphore.release()

if __name__ == "__main__":
    semaphore=threading.BoundedSemaphore(5)
    for i in range(100):
        t=threading.Thread(target=work,args=(i+1,))
        t.start()

#active_count獲取當(dāng)前正在運(yùn)行的線程數(shù)
while threading.active_count()!=1:
        pass
    else:
        print("end")

結(jié)果為:每5次打印停頓一下,直到結(jié)束。

GIL全局解釋器鎖

說到多線程,不得不提一下GIL。GIL的全稱是Global Interpreter Lock(全局解釋器鎖),這是python設(shè)計(jì)之初,為了數(shù)據(jù)安全所做的決定。某個(gè)線程想要執(zhí)行,必須先拿到GIL,并且在一個(gè)進(jìn)程中,GIL只有一個(gè)。只有拿到GIL的線程,才能進(jìn)入CPU執(zhí)行。GIL只在cpython中才有,因?yàn)閏python調(diào)用的是c語言的原生線程,所以他不能直接操作cpu,只能利用GIL保證同一時(shí)間只能有一個(gè)線程拿到數(shù)據(jù)。而在pypy和jpython中是沒有GIL的。

到此,關(guān)于“Python多線程爬蟲舉例分析”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)砀鄬?shí)用的文章!

向AI問一下細(xì)節(jié)

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

AI