簡體   English   中英

多線程迭代器

[英]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 時,才會並行計算/生成value1value2 但是,在我的問題,這需要很長的時間來計算/生成value1value2 ,但它也需要很長的時間,過程value1value2 因此,當我的軟件處理value1value2 ,我希望它並行計算新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被計算/生成,而value1value2被返回以進行處理。

  • 這夠清楚了嗎?
  • 如果是,我怎么能做這樣的同步?

在此先感謝您的幫助!

PS:我需要while 1 ,無需對此發表評論。

不確定我是否完全理解您要做什么,如果您嘗試並行計算value1value2 ,則可以使用multiprocessingthreading 如果任務受 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.ThreadQueue.Queue來替換multiprocessing.Processmultiprocessing.Queue

編輯:現在讓我們讓cal_value1cal_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.futuresProcesPoolExecutor )將計算分散到多個進程中。 在實現中有這兩者的例子。

下面是我使用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.

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