簡體   English   中英

Python - 使用Queue時不會關閉多處理線程

[英]Python - multiprocessing threads don't close when using Queue

這適用於Python 3.x.

我正在以300塊的形式從CSV文件加載記錄,然后生成工作線程以將它們提交到REST API。 我將HTTP響應保存在隊列中,這樣我就可以在處理完整個CSV文件后獲得跳過記錄數的計數。 但是,在我向我的工作人員添加了一個隊列后,線程似乎不再關閉了。 我想監視線程的數量有兩個原因:(1)一旦完成,我可以計算並顯示跳過計數和(2)我想增強我的腳本產生不超過20個左右的線程,所以我不要耗盡內存。

我有兩個問題:

  • 有人可以解釋為什么線程在使用q.put()時保持活動狀態?
  • 是否有不同的方法來管理線程數,並監控是否所有線程都已完成?

這是我的代碼(有些簡化,因為我無法分享我正在調用的API的確切細節):

import requests, json, csv, time, datetime, multiprocessing

TEST_FILE = 'file.csv'

def read_test_data(path, chunksize=300):
    leads = []
    with open(path, 'rU') as data:
        reader = csv.DictReader(data)
        for index, row in enumerate(reader):
            if (index % chunksize == 0 and index > 0):
                yield leads
                del leads[:]
            leads.append(row)
        yield leads

def worker(leads, q):
    payload = {"action":"createOrUpdate","input":leads}
    r = requests.post(url, params=params, data=json.dumps(payload), headers=headers)
    q.put(r.text) # this puts the response in a queue for later analysis
    return

if __name__ == "__main__":
    q = multiprocessing.Queue() # this is a queue to put all HTTP responses in, so we count the skips
    jobs = []
    for leads in read_test_data(TEST_FILE): # This function reads a CSV file and provides 300 records at a time
        p = multiprocessing.Process(target=worker, args=(leads,q,))
        jobs.append(p)
        p.start()
    time.sleep(20) # checking if processes are closing automatically (they don't)
    print(len(multiprocessing.active_children())) ## always returns the number of threads. If I remove 'q.put' from worker, it returns 0

    # The intent is to wait until all workers are done, but it results in an infinite loop
    # when I remove 'q.put' in the worker it works fine
    #while len(multiprocessing.active_children()) > 0:  # 
    #    time.sleep(1)

    skipped_count = 0
    while not q.empty(): # calculate number of skipped records based on the HTTP responses in the queue
        http_response = json.loads(q.get())
        for i in http_response['result']:
            if (i['status'] == "skipped" and i['reasons'][0]['code'] == "1004"):
                skipped_count += 1
    print("Number of records skipped: " + str(skipped_count))

這很可能是因為這個記錄的multiprocessing.Queue怪癖.Queue:

請記住,將項目放入隊列的進程將在終止之前等待,直到所有緩沖的項目由“feeder”線程提供給底層管道。 (子進程可以調用隊列的cancel_join_thread()方法以避免此行為。)

這意味着無論何時使用隊列,您都需要確保在加入進程之前最終刪除已放入隊列的所有項目。 否則,您無法確定已將項目放入隊列的進程將終止。 還要記住,非守護進程會自動加入。

基本上,你需要確保你get()所有從項目Queue ,以保證所有的流程put東西放到該Queue就可以退出。

我認為在這種情況下,您最好使用multiprocessing.Pool ,並將所有作業提交到multiprocessing.Pool.map 這大大簡化了事情,並使您可以完全控制運行的進程數:

def worker(leads):
    payload = {"action":"createOrUpdate","input":leads}
    r = requests.post(url, params=params, data=json.dumps(payload), headers=headers)
    return r.text

if __name__ == "__main__":
    pool = multiprocessing.Pool(multiprocessing.cpu_count() * 2)  # cpu_count() * 2 processes running in the pool
    responses = pool.map(worker, read_test_data(TEST_FILE))

    skipped_count = 0
    for raw_response in responses:
        http_response = json.loads(raw_response)
        for i in http_response['result']:
            if (i['status'] == "skipped" and i['reasons'][0]['code'] == "1004"):
                skipped_count += 1
    print("Number of records skipped: " + str(skipped_count))

如果您擔心將read_test_data(TEST_FILE)轉換為列表(使用Pool.map所需read_test_data(TEST_FILE)的內存成本,則可以使用Pool.imap

編輯:

正如我在上面的評論中提到的,這個用例看起來像是I / O綁定的,這意味着你可以通過使用multiprocessing.dummy.Pool (它使用線程池而不是進程池)來看到更好的性能。 試一試,看看哪個更快。

暫無
暫無

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

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