1

我正试图让它到达我的线程可以捕捉到信号的地方。在我看来,kill_received单例列表位于和的相同命名空间中signal_handler()do_the_uploads() 并且引用了相同的内存位置。但是当我在运行时控制 C 时,我看到 False 从"print kill_received[0]"它应该为 True 的时候开始打印,因为我点击了 control-C。

kill_received = [False]

def signal_handler(signal, frame):
    global kill_received
    kill_received[0] = True
    print "\nYou pressed Ctrl+C!"
    print (
        "Your logs and their locations are:"
        "\n{}\n{}\n{}".format(debug, error, info))
    sys.exit(0)

def do_the_uploads(file_list, file_quantity,
        retry_list, authenticate):
    """The uploading engine"""
    value = raw_input(
        "\nPlease enter how many conncurent "
        "uploads you want at one time(example: 200)> ")
    value = int(value)
    logger.info('{} conncurent uploads will be used.'.format(value))

    confirm = raw_input(
        "\nProceed to upload files? Enter [Y/y] for yes: ").upper()
    if confirm == "Y":
        kill_received = False
        sys.stdout.write("\x1b[2J\x1b[H")
        q = Queue.Queue()

        def worker():
            global kill_received
            while True and not kill_received[0]:
                print kill_received[0]
                item = q.get()
                upload_file(item, file_quantity, retry_list, authenticate)
                q.task_done()

        for i in range(value):
            t = Thread(target=worker)
            t.setDaemon(True)
            t.start()

        for item in file_list:
            q.put(item)

        q.join()
        print "Finished. Cleaning up processes...",
        #Allowing the threads to cleanup
        time.sleep(4)
        print "done."

从主脚本:

from modules.upload_actions import do_the_uploads, retry, signal_handler

if __name__ == '__main__':
    signal.signal(signal.SIGINT, signal_handler)

    retry_list = []
    file_list, authenticate, ticket_number = main()
    file_quantity = FileQuantity(len(file_list))
    do_the_uploads(file_list, file_quantity, 
            retry_list, authenticate)

更新:

仍然没有成功,但我将语法更改为这样,因为它更简洁:

   def worker():
        global kill_received
        while not kill_received[0]:
            time.sleep(1)
            print kill_received[0]
            item = q.get()
            upload_file(item, file_quantity, retry_list, authenticate)
            q.task_done()
4

2 回答 2

3

了解正在发生的事情的关键是您所做的评论

不。当线程全部完成时,我会这样做......但不是在线程执行期间他们必须上传的所有文件。

这行代码:

q.join()

与您可能期望的相反,control-C 不会导致它停止等待队列 - 在此调用返回之前它不会接受 control-C。所以发生的事情是你所有的线程都完成了他们的工作并清空了队列,然后在线等待

item = q.get()

只有在最后一个线程调用之后q.task_done,主线程才会返回,然后处理 control-C。但是,此时所有线程都卡在等待队列中的更多项目(它们不会得到),因此它们永远不会退出循环。

这里可能会发生比这更多的事情,但要查看这是否是问题,请尝试忙等待队列为空:

while not q.empty():
    time.sleep(0.1)
q.join()

之后您需要加入,因为队列为空意味着最后一次上传已从队列中拉出,而不是已完成。

您可以添加的另一件事是向队列中添加一个指示线程应该完成的项目,例如None. 例如,

    def worker():
        global kill_received
        while True and not kill_received[0]:
            print kill_received[0]
            item = q.get()
            if item is None:
                q.task_done()
                break
            upload_file(item, file_quantity, retry_list, authenticate)
            q.task_done()

    for i in range(value):
        t = Thread(target=worker)
        t.setDaemon(True)
        t.start()

    for item in file_list:
        q.put(item)

    for i in range(value):
        q.put(None)

当然,这假定这None不是要上传的有效值。这对 control-C 问题没有帮助,但您可能会发现它有助于确保程序正常完成时线程退出。

作为一般帮助,当您使用线程测试事物时,有一种方法可以打印出所有线程的堆栈跟踪信息会很有帮助。这个 SO question讨论了如何做到这一点。

于 2013-11-27T22:16:00.063 回答
2

您看到 False 打印的原因是因为它永远没有机会打印它。你在它每一次击中你的print kill_received[0]陈述之前就把它杀死了。

想想看。在执行此语句之间可能有很小的机会可以按 Ctrl-C:

while True and not kill_received[0]:

和这个声明:

print kill_received[0]

但这是不可能的。在任何其他时间中断任何线程将导致它们停止循环(从您的while语句中),并且永远不会打印任何内容。


编辑: 你有这条线:kill_received = False这可能会导致你的问题。应该是kill_received[0] = False

于 2013-11-27T21:42:06.657 回答