有时,即使循环任务结束,循环线程也会被卡住而没有结束

2024-10-03 11:14:53 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在进行我的第一个线程化python项目 我有一个线程的问题,有时它工作,但有时它没有结束,为什么

我的代码示例

import threading,signal,sys,queue,time

def write(text):
  sys.stdout.write(text + "\n")
  sys.stdout.flush()


class runThread(threading.Thread):

     def __init__(self):
         threading.Thread.__init__(self)
         self.daemon = True

     def run(self):
         while not isKilled(): # used if user use ctrl_c to stop the task before finish
             if  numbers.empty(): # task has finished
                 break
             num = numbers.get()
             write("[*] Thread-{} : number: {}".format(self.ident,num))
             time.sleep(1)

def mainThread():
   for _ in range(5):
      thread = runThread()
      thread.start()
      THREADS.append(thread)
   for t in THREADS: t.join()



def start():
  global event
  global kill
  global isKilled
  global handler
  global THREADS
  global numbers 

  event = threading.Event()
  kill = lambda : event.set()
  isKilled = lambda : event.isSet()
  handler = lambda sig,fream: kill()

  THREADS = []
  numbers = queue.Queue()

  for n in range(1000):
      numbers.put(n)

  runMainThread = threading.Thread(target=mainThread)
  runMainThread.daemon = True
  runMainThread.start()
  signal.signal(signal.SIGINT, handler)
  signal.signal(signal.SIGTERM,handler)

  while runMainThread.isAlive():
      continue
  print("[*] Done")
  runMainThread.join()
if __name__=="__main__":
    start()

有时它就像一个符咒,但有时它在最后一次打印数字后卡住了 而且没有去print('[*] Done')


Tags: selfeventsignaldefsysglobalthreadstart
1条回答
网友
1楼 · 发布于 2024-10-03 11:14:53

当多个线程执行以下代码时:

if numbers.empty(): # task has finished
   break 
num = numbers.get()

想象一下:假设numbers只剩下一个数字。thread1执行numbers.empty(),返回False。然后,在thread1执行numbers.get()之前,thread2被安排运行。thread2执行numbers.empty(),它还返回False,因为最后一个元素还没有执行。thread2执行numbers.get()并获取最后一个元素。然后thread1将永远阻塞numbers.get()

根据文件here

Queue.empty() Return True if the queue is empty, False otherwise. If empty() returns True it doesn’t guarantee that a subsequent call to put() will not block. Similarly, if empty() returns False it doesn’t guarantee that a subsequent call to get() will not block.

解决办法是:

  1. 设置numbers.get()的超时:

    try:
        num = numbers.get(timeout=5) # wait 5 seconds at most.
    except queue.Empty:
        break
    
  2. 使用threading.Lock确保线程在numbers.empty()numbers.get()之间时不会相互中断

    # in start()
    global lock
    lock = threading.Lock()
    
    # in runThread.run()
    lock.acquire()
    if numbers.empty():
        lock.release()
        break
    num = numbers.get()
    lock.release()
    write("[*] Thread-{} : number: {}".format(self.ident,num))
    

相关问题 更多 >