在KeyboardInterrupt时,BoundedSemaphore在线程中挂起

2 投票
5 回答
3425 浏览
提问于 2025-04-15 11:58

如果你在尝试获取一个信号量的时候按下了键盘中断(比如 Ctrl+C),那么其他也在尝试释放这个信号量的线程就会一直卡在那里,无法继续。

代码:

import threading
import time

def worker(i, sema):
    time.sleep(2)
    print i, "finished"
    sema.release()


sema = threading.BoundedSemaphore(value=5)
threads = []
for x in xrange(100):
    sema.acquire()
    t = threading.Thread(target=worker, args=(x, sema))
    t.start()
    threads.append(t)

启动这个程序,然后在它运行的时候按 Ctrl+C。它会卡住,永远不会退出。

0 finished
3 finished
1 finished
2 finished
4 finished
^C5 finished
Traceback (most recent call last):
  File "/tmp/proof.py", line 15, in <module>
    sema.acquire()
  File "/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/threading.py", line 290, in acquire
    self.__cond.wait()
  File "/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/threading.py", line 214, in wait
    waiter.acquire()
KeyboardInterrupt
6 finished
7 finished
8 finished
9 finished

我该怎么做才能让最后几个线程正常结束,然后程序正常退出呢?(如果你不去中断它,它是可以正常退出的)

5 个回答

0

在这种情况下,看起来你可能只想用一个线程池来控制线程的启动和停止。你可以使用Chris Arndt的线程池库,用法大概是这样的:

pool = ThreadPool(5)
try:
    # enqueue 100 worker threads
    pool.wait()
except KeyboardInterrupt, k:
    pool.dismiss(5)
    # the program will exit after all running threads are complete
1

在你原来的代码中,你还可以把线程设置为守护线程。当你中断脚本时,这些守护线程就会像你预期的那样全部结束。

  t = ...
  t.setDaemon(True)
  t.start()
2

你可以使用信号模块来设置一个标志,这个标志会告诉主线程停止处理:

import threading
import time
import signal
import sys

sigint = False

def sighandler(num, frame):
  global sigint
  sigint = True

def worker(i, sema):
  time.sleep(2)
  print i, "finished"
  sema.release()

signal.signal(signal.SIGINT, sighandler)
sema = threading.BoundedSemaphore(value=5)
threads = []
for x in xrange(100):
  sema.acquire()
  if sigint:
    sys.exit()
  t = threading.Thread(target=worker, args=(x, sema))
  t.start()
  t.join() 
  threads.append(t)

撰写回答