Python:进程在futex(0x2a5fcc0,FUTEX_WAIT_PRIVATE,0,NULL在多线程中挂起

use*_*759 3 python queue multithreading futex

所以我有一个队列:

q = Queue.Queue()
Run Code Online (Sandbox Code Playgroud)

我在里面放一些东西。

items = ["First", "Second"]
for val in items:
    q.put(val)
Run Code Online (Sandbox Code Playgroud)

我正在生成15个线程。

for i in range(15):
   tname = 't-%s' % i
   t = my_thread(some_func, q, tname)
   t.start()

q.join()
Run Code Online (Sandbox Code Playgroud)

my_thread类的外观如下:

class my_thread(threading.Thread):
    def __init__(self, some_func, q_, name=''):
       threading.Thread.__init__(self)
       self.func = some_func
       self.process_q = q_
       self.name = name
       self.prefix = name


    def run(self):
       stime = time.time()
       logging.info('%s thread staring at : %s' % (threading.currentThread().getname(), time.ctime(stime)))
       while True:
           if self.process_q.empty():
               break
           queue_item = self.process_q.get()
           self.name = self.prefix + '-' + queue_item
           try:
               #run the function
           except Exception as e:
               logging.error('Caught some error')
           finally:
               self.process_q.task_done()


       endTime = time.time()
       logging.info('%s thread finished at: %s' % (threading.currentThread().getName(), time.ctime(endTime)))
Run Code Online (Sandbox Code Playgroud)

如果我看一下日志,我看到的是两个或多个线程同时访问Queue,而当Queue为空时,while循环不会中断。

假设t-0线程"first"已从队列中取出项目。但是,t-2线程可能采取"second"前项t-1线程可以把它从而使队列空...但是当t-1做了self.process_q.empty()检查,队列不为空。因此t-1线程永远不会退出/结束,而会挂起。

如果我对进程ID进行跟踪,则会得到以下结果:

Process 13307 attached
futex(0x2a5fcc0, FUTEX_WAIT_PRIVATE, 0, NULL
Run Code Online (Sandbox Code Playgroud)

我该如何解决???

st0*_*0ne 5

您的线程随机挂在阻塞的self.process_q.get()函数中。-> 种族条件

此刻,线程已启动,队列不为空。代码部分...

...
if self.process_q.empty():
    break
queue_item = self.process_q.get()
...
Run Code Online (Sandbox Code Playgroud)

未在所有线程上同步。因此可以通过if条件超过2个线程(队列大小= 2)。两个线程正在从self.process_q.get()函数获取结果,而其他线程正在阻塞并等待队列中的结果。

直到所有非守护进程线程都完成后,python程序才能退出。因此它永远挂起。

考虑将线程设置为守护程序模式:

for i in range(15):
    tname = 't-%s' % i
    t = my_thread(some_func, q, tname)
    t.setDaemon(True)
    t.start()
Run Code Online (Sandbox Code Playgroud)

来自https://docs.python.org/2/library/threading.html#threading.Thread.daemon

守护程序

一个布尔值,指示此线程是否是守护程序线程(真)(假)。必须在调用start()之前设置此项,否则会引发RuntimeError。它的初始值是从创建线程继承的;主线程不是守护程序线程,因此在主线程中创建的所有线程默认为daemon = False。

当没有活动的非守护线程时,整个Python程序将退出。

通过将daemon-mode设置为true,程序将在队列为空之后退出(q.join())。