簡體   English   中英

同時在python中運行多個線程 - 這可能嗎?

[英]running multiple threads in python, simultaneously - is it possible?

我正在編寫一個應該多次獲取URL的小爬蟲,我希望所有的線程同時運行(同時)。

我寫了一小段應該這樣做的代碼。

import thread
from urllib2 import Request, urlopen, URLError, HTTPError


def getPAGE(FetchAddress):
    attempts = 0
    while attempts < 2:
        req = Request(FetchAddress, None)
        try:
            response = urlopen(req, timeout = 8) #fetching the url
            print "fetched url %s" % FetchAddress
        except HTTPError, e:
            print 'The server didn\'t do the request.'
            print 'Error code: ', str(e.code) + "  address: " + FetchAddress
            time.sleep(4)
            attempts += 1
        except URLError, e:
            print 'Failed to reach the server.'
            print 'Reason: ', str(e.reason) + "  address: " + FetchAddress
            time.sleep(4)
            attempts += 1
        except Exception, e:
            print 'Something bad happened in gatPAGE.'
            print 'Reason: ', str(e.reason) + "  address: " + FetchAddress
            time.sleep(4)
            attempts += 1
        else:
            try:
                return response.read()
            except:
                "there was an error with response.read()"
                return None
    return None

url = ("http://www.domain.com",)

for i in range(1,50):
    thread.start_new_thread(getPAGE, url)

從apache日志來看,似乎線程並不是同時運行,請求之間有一點差距,它幾乎檢測不到但我可以看到線程並不是真正的並行。

我讀過GIL,有沒有辦法繞過它而不用調用C \\ C ++代碼? 我真的不明白GIL如何實現線程化? python基本上解釋了下一個線程一旦完成前一個線程?

謝謝。

正如您所指出的,GIL經常阻止Python線程並行運行。

然而,情況並非總是如此。 I / O綁定代碼是一個例外。 當一個線程正在等待I / O請求完成時,它通常會在進入等待之前釋放GIL。 這意味着其他線程可以在此期間取得進展。

但是,一般而言,當需要真正的並行性時, multiprocessing是更安全的選擇。

我讀過GIL,有沒有辦法繞過它而不用調用C \\ C ++代碼?

並不是的。 通過ctypes調用的函數將在這些調用期間釋放GIL。 執行阻塞I / O的函數也會釋放它。 還有其他類似的情況,但它們總是涉及主Python解釋器循環之外的代碼。 你不能放棄Python代碼中的GIL。

您可以使用這樣的方法來創建所有線程,讓它們等待條件對象,然后讓它們開始“ 同時 ”獲取URL:

#!/usr/bin/env python
import threading
import datetime
import urllib2

allgo = threading.Condition()

class ThreadClass(threading.Thread):
    def run(self):
        allgo.acquire()
        allgo.wait()
        allgo.release()
        print "%s at %s\n" % (self.getName(), datetime.datetime.now())
        url = urllib2.urlopen("http://www.ibm.com")

for i in range(50):
    t = ThreadClass()
    t.start()

allgo.acquire()
allgo.notify_all()
allgo.release()

這會讓你更接近於同時發生所有提取, 但是

  • 離開計算機的網絡數據包將按順序通過以太網線傳輸,而不是同時傳遞,
  • 即使您的計算機上有16個以上的核心,您的計算機和Web主機之間的某些路由器,網橋,調制解調器或其他設備也可能擁有較少的核心,並且可能會對您的請求進行序列化,
  • 您從中獲取內容的Web服務器將使用accept()調用來響應您的請求。 對於正確的行為,使用服務器全局鎖實現,以確保只有一個服務器進程/線程響應您的查詢。 即使您的某些請求同時到達服務器,也會導致某些序列化。

您可能會在更大程度上獲得重疊請求(即其他人在完成之前開始),但您永遠不會在服務器上同時啟動所有請求。

如果您使用Jython或IronPython(以及未來的PyPy)運行代碼,它將並行運行

你還可以看看像pypy的未來,我們將擁有軟件過渡記憶(從而廢除GIL)這一切只是研究和知識分子嘲笑,但它可能會成長為一個大的東西。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM