[英]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.