[英]Iterator with multithreading
我需要實現一個返回兩個值的Iterator
(到目前為止沒有什么異常),但是這些值需要並行連續計算/生成,即使沒有請求迭代器。
這是一個解釋我需要什么的例子。
def GenerateValues()
#I do the math for value1 in the first thread
#I do the math for value2 in the second thread
return value1 value2
def myIterator()
while 1:
yield GenerateValues()
在這種情況下,僅當調用函數 myIterator 時,才會並行計算/生成value1
和value2
。 但是,在我的問題,這需要很長的時間來計算/生成value1
和value2
,但它也需要很長的時間,過程value1
和value2
。 因此,當我的軟件處理value1
和value2
,我希望它並行計算新value1
和新value2
。
所以它會是這樣的:
def GenerateValues()
#If value1 and value 2 are not computed, then wait.
#I do the math for the new value1 in the first thread without blocking
#I do the math for the new value2 in the second thread without blocking
return value1 value2
def myIterator()
while 1:
yield GenerateValues()
通過這樣的配置,新的value1
和 new value2
被計算/生成,而value1
和value2
被返回以進行處理。
在此先感謝您的幫助!
PS:我需要while 1
,無需對此發表評論。
不確定我是否完全理解您要做什么,如果您嘗試並行計算value1
和value2
,則可以使用multiprocessing
或threading
。 如果任務受 CPU 限制,我建議multiprocessing
,以充分利用您的 CPU 通過使用子進程而不是線程來“回避”全局解釋器鎖 (GIL)。
這是使用multiprocessing
一個相當直接的例子:
from multiprocessing import Queue, Process
def cal_value1(queue):
# do the job
queue.put({'value1': value1})
def cal_value2(queue):
# do the job
queue.put({'value2': value2})
def GenerateValues()
#If value1 and value 2 are not computed, then wait.
queue = Queue() #
process_1 = Process(target=cal_value1, args=(queue, ))
process_2 = Process(target=cal_value2, args=(queue, ))
process_1.start()
process_2.start() # start both processes
process_1.join()
process_2.join() # wait for both to finish
result = queue.get()
result.update(queue.get()) # get results
return result['value1'], result['value2']
PS 如果需要,您可以輕松使用threading.Thread
和Queue.Queue
來替換multiprocessing.Process
和multiprocessing.Queue
。
編輯:現在讓我們讓cal_value1
和cal_value2
長時間運行進程,您可能希望在腳本的開頭啟動這兩個進程。
from multiprocessing import Queue, Process
def cal_value1(tasks, results):
while True:
task = tasks.get() # this will block until a new task coming in
# calculate value1
results.put({'value1': value1})
def cal_value2(tasks, results):
while True:
task = tasks.get() # this will block until a new task coming in
# calculate value2
results.put({'value2': value2})
def main():
cal_value1_tasks, cal_value2_tasks, results = Queue(), Queue(), Queue()
process_1 = Process(target=cal_value1, args=(cal_value1_tasks, results, ))
process_2 = Process(target=cal_value2, args=(cal_value2_tasks, results, ))
process_1.start()
process_2.start()
cal_value1_tasks.put('cal_value1')
cal_value2_tasks.put('cal_value2') # Start to calculate the first pair
values = GenerateValues(cal_value1_tasks, cal_value2_tasks, results)
def GenerateValues(cal_value1_tasks, cal_value2_tasks, results):
values = results.get() # get results
values.update(queue.get()) # notice that it'll block until both value1 and value 2 calculated
cal_value1_tasks.put('cal_value1')
cal_value2_tasks.put('cal_value2') # before returning, start to calculate the next round of value1 and value2
return values['value1'], values['value2]
我不確定我是否正確回答了您的問題。 另外,我不明白為什么要創建一個額外的迭代器而不是直接在 ComputeValues 中產生。 但我會讓我的例子靠近你的代碼。
如果是這樣,也許您想嘗試像這樣使用 multiprocessing.pool:
from multiprocessing import Pool
def ComputeValues(v1,v2):
...
return value1 value2 # you could just yield here!
def myIterator(x): #x is a tuple in this case
v1,v2 = x
while 1:
yield ComputeValues(v1,v2)
p = Pool(5) #will spawn 5 processes, each of them will run myIterator
print(p.imap_unordered(myIterator, [(v1a,v2a), (v1b, v2b), ...]))
在 CPython(您從 python.org 獲得的最常用的實現)中,線程並不能真正幫助並行化在 python 中完成的計算。
因為為了使內存管理更容易(除其他外),一次只有一個線程可以執行 Python 字節碼。 這是由全局解釋器鎖(“GIL”)強制執行的。
(如果您在像numpy
這樣的擴展中進行所有計算,它在工作時釋放 GIL,則此限制通常不適用)
您可以使用multiprocessing
或(或 Python 3.2 以后的concurrent.futures
的ProcesPoolExecutor
)將計算分散到多個進程中。 在實現中有這兩者的例子。
下面是我使用ProcessPoolExecutor
將 DICOM 圖像轉換為 JPEG 的示例。 它使用“魔杖”python 綁定到ImageMagick 。 制作一份工作(期貨)清單並開始這些工作是做什么的。 as_completed
函數按照它們完成的順序返回每個未來的結果。
def convert(filename):
"""Convert a DICOM file to a JPEG file, removing the blank areas from the
Philips x-ray detector.
Arguments:
filename: name of the file to convert.
Returns:
Tuple of (input filename, output filename)
"""
outname = filename.strip() + '.jpg'
with Image(filename=filename) as img:
with img.convert('jpg') as converted:
converted.units = 'pixelsperinch'
converted.resolution = (300, 300)
converted.crop(left=232, top=0, width=1574, height=2048)
converted.save(filename=outname)
return filename, outname
def main(argv):
"""Main entry point for dicom2jpg.py.
Arguments:
argv: command line arguments
"""
if len(argv) == 1:
binary = os.path.basename(argv[0])
print("{} ver. {}".format(binary, __version__), file=sys.stderr)
print("Usage: {} [file ...]\n".format(binary), file=sys.stderr)
print(__doc__)
sys.exit(0)
del argv[0] # Remove the name of the script from the arguments.
es = 'Finished conversion of {} to {}'
with cf.ProcessPoolExecutor(max_workers=os.cpu_count()) as tp:
fl = [tp.submit(convert, fn) for fn in argv]
for fut in cf.as_completed(fl):
infn, outfn = fut.result()
print(es.format(infn, outfn))
您可以在我在 github 上的腳本存儲庫中找到此示例和其他示例。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.