Python脚本在多线程后挂起

时间:2018-04-10 01:23:02

标签: python multithreading

我知道在Python中挂起线程有一些问题和答案,但是我的情况略有不同,因为脚本在所有线程完成后都挂起了。线程脚本如下,但显然前两个函数被大量简化。

当我运行显示的脚本时,它可以工作。当我使用我的实际功能时,脚本会在最后一行之后挂起。因此,处理所有方案(并打印一条消息以确认),logStudyData()然后整理所有结果并写入csv。打印"Script Complete"。然后它挂了。

删除了线程功能的脚本运行正常。

我已尝试将主脚本封装在try...except中,但不会记录任何异常。如果我在最终print上使用带有断点的调试器,然后将其向前移动,则会挂起。

我知道这里没什么可说的,但是不包括整个1500行的剧本,我不知道还有别的事要做。欢迎任何建议!

def runScenario(scenario):
    # Do a bunch of stuff
    with lock:
        # access global variables
        pass
    pass

def logStudyData():
    # Combine results from all scenarios into a df and write to csv
    pass

def worker():
    global q
    while True:
        next_scenario = q.get() 
        if next_scenario is None:
            break
        runScenario(next_scenario)
        print(next_scenario , " is complete")
        q.task_done() 

import threading
from queue import Queue 
global q, lock
q = Queue()  
threads = []

scenario_list = ['s1','s2','s3','s4','s5','s6','s7','s8','s9','s10','s11','s12']
num_worker_threads = 6  
    lock = threading.Lock()
    for i in range(num_worker_threads):
        print("Thread number ",i)
        this_thread = threading.Thread(target=worker)
        this_thread.start()
        threads.append(this_thread)
    for scenario_name in scenario_list:
        q.put(scenario_name)  
    q.join()
    print("q.join completed")
    logStudyData() 
    print("script complete")

1 个答案:

答案 0 :(得分:1)

正如Queue.get的文档所说:

  

从队列中删除并返回一个项目。如果可选的参数block为真且timeoutNone(默认值),则必要时阻止该项目可用。如果timeout是正数,则它会阻止最多超时秒,如果在该时间内没有可用项,则会引发Empty异常。否则(block为false),如果一个项立即可用,则返回一个项,否则引发Empty异常(在这种情况下忽略超时)。

换句话说,get无法返回None,除非您在主线程上调用q.put(None),但您不会这样做。

请注意,这些文档正下方的示例执行此操作:

for i in range(num_worker_threads):
    q.put(None)
for t in threads:
    t.join()

第二个在技术上是必要的,但你通常不会这样做。

但第一个是绝对必要的。您需要执行此操作,或者提出其他一些机制来告诉您的员工退出。没有它,你的主线程只是试图退出,这意味着它试图加入每个工人,但这些工人都被get永远封锁,永远不会发生,所以你的程序会永远挂起。

建立一个线程池可能不是火箭科学(如果只是因为火箭科学家往往需要他们的计算是确定性的和实时的硬件......),但它也不是微不足道的,并且有很多事情你可能会出错。您可能需要考虑使用Python标准库中的两个已构建的线程池之一concurrent.futures.ThreadPoolExecutormultiprocessing.dummy.Pool。这会将整个程序减少到:

import concurrent.futures

def work(scenario):
    runScenario(scenario)
    print(scenario , " is complete")

scenario_list = ['s1','s2','s3','s4','s5','s6','s7','s8','s9','s10','s11','s12']
with concurrent.futures.ThreadPoolExecutor(max_workers=6) as x:
    results = list(x.map(work, scenario_list))
print("q.join completed")
logStudyData() 
print("script complete")

显然你仍然需要锁定你在runScenario内改变的任何可变变量 - 尽管如果你只是在那里使用一个可变变量,因为你无法弄清楚如何将值返回给主线程,使用Executor只需要return,只需work for result in x.map(work, scenario_list): do_something(result) ,然后您可以像这样使用它们:

API ACCESS