簡體   English   中英

Python線程stdin / stdout

[英]Python Threading stdin/stdout

我有一個包含大量數據的文件。 每行都是一條記錄。 我試圖對整個文件做一些ETL工作。 現在我正在使用標准輸入逐行讀取數據。 關於這一點很酷的是你的腳本可以非常靈活地與其他腳本和shell命令集成。 我將結果寫入標准輸出。 例如。

$ cat input_file
line1 
line2
line3
line4
...

我當前的python代碼如下所示 - parse.py

import sys
for line in sys.stdin:
    result = ETL(line)    # ETL is some self defined function which takes a while to execute.
    print result

下面的代碼是它現在的工作方式:

cat input_file | python parse.py > output_file

我查看了Python的Threading模塊,我想知道如果我使用該模塊,性能是否會得到顯着改善。

問題1: 我應該如何規划每個線程的配額,為什么?

...
counter = 0
buffer = []
for line in sys.stdin:
    buffer.append(line)
    if counter % 5 == 0:   # maybe assign 5 rows to each thread? if not, is there a rule of thumb to determine
        counter = 0
        thread = parser(buffer)
        buffer = []
        thread.start() 

問題2: 多線程可能會同時將結果打印回stdout,如何組織它們並避免下面的情況?

import threading
import time

class parser(threading.Thread):
    def __init__ (self, data_input):
        threading.Thread.__init__(self)
        self.data_input = data_input

    def run(self):
        for elem in self.data_input:
            time.sleep(3)
            print elem + 'Finished'

work = ['a', 'b', 'c', 'd', 'e', 'f']

thread1 = parser(['a', 'b'])  
thread2 = parser(['c', 'd'])
thread3 = parser(['e', 'f'])

thread1.start()
thread2.start()
thread3.start()   

輸出真的很難看,其中一行包含兩個線程的輸出。

aFinished
cFinishedeFinished

bFinished
fFinished
dFinished

首先提出第二個問題,這就是互斥體的用途。 您可以通過使用鎖來協調解析器之間來獲得所需的更清晰的輸出,並確保在給定的時間段內只有一個線程可以訪問輸出流:

class parser(threading.Thread):
    output_lock = threading.Lock()

    def __init__ (self, data_input):
        threading.Thread.__init__(self)
        self.data_input = data_input

    def run(self):
        for elem in self.data_input:
            time.sleep(3)
            with self.output_lock:
                print elem + 'Finished'

關於您的第一個問題,請注意,多線程可能無法為您的特定工作負載帶來任何好處。 這在很大程度上取決於您對每個輸入行(您的ETL函數)所做的工作是主要是CPU綁定還是IO綁定。 如果是前者(我懷疑可能),由於全局解釋器鎖定 ,線程將無濟於事。 在這種情況下,您可能希望使用multiprocessing模塊在多個進程之間分配工作而不是多個線程。

但是您可以通過更容易實現的工作流程獲得相同的結果:將輸入文件拆分為n (使用例如split命令); 在每個子文件上分別調用extract-and-transform腳本; 然后連接生成的輸出文件。

一個挑剔:“使用標准輸入逐行讀取數據,因為它不會將整個文件加載到內存中”涉及一個誤解。 您可以在Python中逐行讀取文件,例如,將sys.stdin替換為以下sys.stdin中的文件對象:

for line in sys.stdin:

另請參見文件對象的readline()方法,並注意read()可以將要讀取的最大字節數作為參數。

無論線程是否有用,您都高度依賴於您的情況。 特別是,如果您的ETL()函數涉及大量磁盤訪問,那么線程可能會給您帶來非常顯着的速度提升。

在回答你的第一個問題時,我總是發現它只是取決於你。 在確定理想的線程數時,有許多因素在起作用,其中許多因素與程序有關。 例如,如果您正在進行大量磁盤訪問(這非常慢),那么您將需要更多線程在等待磁盤訪問時利用停機時間。 但是,如果該程序受CPU限制,那么大量的線程可能不會非常有用。 因此,盡管可以分析所有因素以得出理想數量的線程,但通常可以更快地進行初始猜測,然后從那里進行調整。

更具體地說,為每個線程分配一定數量的行可能不是分配工作的最佳方式。 例如,考慮一行是否需要特別長的時間來處理。 最好是一個線程可以在那一行上工作,而其他線程可以在此期間再做幾行。 處理此問題的最佳方法是使用隊列。 如果將每一行推入隊列,則每個線程都可以從隊列中拉出一行,處理它,然后重復,直到隊列為空。 通過這種方式,工作得到分配,使得沒有線程沒有工作要做(當然,直到最后)。

現在,第二個問題。 你肯定從多個線程寫入stdout並不是一個理想的解決方案。 理想情況下,您可以安排一些事情,以便只在一個地方寫入stdout。 一個很好的方法是使用隊列。 如果你讓每個線程將其輸出寫入共享隊列,那么你可以產生一個額外的線程,其唯一的任務是將項目拉出該隊列並將它們打印到stdout。 通過將打印限制為僅一個線程,您將避免多個線程嘗試一次打印時固有的問題。

暫無
暫無

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

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