Проблема Pydev PyUnit при использовании thread.join для обеспечения объединения всех потоков

У меня возникла проблема при использовании pydev для тестирования, когда мои тесты продолжают зависать. Я вникал в проблему и знаю, в чем причина. Я предоставил образцы кода ниже, которые можно использовать для воспроизведения проблемы.

В основном я тестирую Centos 6.3, python 2.7, eclipse juno, pydev 2.7.1, однако проблема также возникает в Windows 7 с аналогичной настройкой.

У меня есть скрипт python, который работает как средство запуска процессов для разных потоков сервера (все внутри сторонних библиотек, поэтому я не могу отказаться от этой стороны системы).

Чтобы убедиться, что все потоки завершены в конце моего process.py, у меня есть раздел кода, который пытается присоединиться ко всем потокам перед выходом.

 for t in threading.enumerate():               
     if t.getName() != 'MainThread':
         t.join()   

Это отлично работает в обычном производственном коде.

Проблема возникает при запуске тестов в PyUnit в eclipse с помощью pydev. В python добавляются дополнительные потоки, что приводит к зависанию моих тестов.

Если я запускаю свою программу с помощью Run As -> Python Run , мой код работает, как и ожидалось, и завершается нормально. Если я запускаю свою программу с помощью Run As -> Python unit-test, тест всегда зависает.

Если я посмотрю, какие темы доступны, проблема станет ясной. Используя предоставленный образец тестового кода, я вижу, что при простом запуске теста в качестве запуска python отображаются следующие потоки (как и ожидалось)

Thread:  <bound method _MainThread.getName of <_MainThread(MainThread, started 140268135126784)>> 
Thread:  <bound method ThreadClass.getName of <ThreadClass(Thread A, started 140268006471424)>>
Thread:  <bound method ThreadClass.getName of <ThreadClass(Thread B, started 140267927631616)>>

Когда я запускаю свои тесты как модульный тест

Thread:  <bound method _MainThread.getName of <_MainThread(MainThread, started 139904571213568)>>
Thread:  <bound method WriterThread.getName of <WriterThread(pydevd.Writer, started daemon 139904379361024)>>
Thread:  <bound method ThreadClass.getName of <ThreadClass(Thread A, started 139904130479872)>>
Thread:  <bound method ThreadClass.getName of <ThreadClass(Thread B, started 139904119990016)>>
Thread:  <bound method PyDBCommandThread.getName of <PyDBCommandThread(pydevd.CommandThread, started daemon 139904358381312)>>
Thread:  <bound method ReaderThread.getName of <ReaderThread(pydevd.Reader, started daemon 139904368871168)>>
Thread:  <bound method ServerComm.getName of <ServerComm(Thread-4, started 139904345736960)>>

Дополнительные потоки, добавленные python, похоже, нарушают этот код. Когда мой код пытается присоединиться к ServerComm или pydev.Writer, он зависает.

Я знаю, что мог бы попытаться не присоединяться к этим потокам по имени, однако таким образом я меняю производственный код, чтобы справиться с этим, и я не слишком заинтересован в этом решении. Кто-нибудь еще сталкивался с этим раньше и нашел хороший обходной путь? Любая помощь в этом будет высоко оценена. Ниже приведен пример кода для проблемы.

Пример test_process.py

import sys
import traceback
import unittest

class TestUBProcessManager(unittest.TestCase):
    def setUp(self):
        pass

    def runTest(self):
        globalsDict = {'sys':sys, '__name__':'__main__'}

        haveException = False
        try:
            execfile('Process.py', globalsDict)
        except Exception, detail:
            haveException = True
            traceback.print_exc()

        self.assertFalse(haveException)

if __name__ == '__main__':
    unittest.main()    

Образец Process.py

import threading                                                                                                    
import time                                                                                                         

class ThreadClass(threading.Thread):                                                                                

    def __init__(self, threadName, threadRunCount,threadSleep):                                                     
        threading.Thread.__init__(self)                                                                             
        self.name = threadName;                                                                                     
        self.runCount = threadRunCount;                                                                             
        self.sleepTime = threadSleep;                                                                               

    def run(self):                                                                                                  
        for i in range (1,self.runCount):                                                                           
            print "\t",self.name, "Working";                                                                        
            time.sleep(self.sleepTime);                                                                             

class Launcher(object):                                                                                             
    def __init__(self):                                                                                             
        print "Init Threads";                                                                                       
        self.threadA = ThreadClass("Thread A",3,2)                                                                  
        self.threadB = ThreadClass("Thread B",7,2)                                                                  

    def launchProcess(self):                                                                                        
        print "Starting Threads";                                                                                   
        self.threadA.start();                                                                                       
        self.threadB.start();                                                                                       
        time.sleep(2);                                                                                              

if __name__ == '__main__':                                                                                          
    launcher = Launcher()                                                                                           
    try:                                                                                                            
        launcher.launchProcess()                                                                                    
    finally:                                                                                                        
        print "Available Threads Needed To Finish"                                                                  
        for t in threading.enumerate():                                                                             
            print "Thread: ",t.getName                                                                              

        print "Attempt to join threads to ensure all threads are finished"                                          
        for t in threading.enumerate():                                                                             
            print "About To Join : ",t.getName                                                                      
            if t.getName() != 'MainThread':                                                                         
                    t.join()                                                                                        
    print "All Done"                                                                                                

person joey.enfield    schedule 05.12.2012    source источник


Ответы (1)


Просто узнайте, что кто-то еще сталкивается с этой проблемой. Я решил изменить свой код, чтобы проверить, какие потоки присутствуют при запуске теста.

if __name__ == '__main__':                                                                                          
    initialThreads = [];    
    for t in threading.enumerate():
        initialThreads.append(t);

    launcher = Launcher()                                                                                           
    try:                                                                                                            
        launcher.launchProcess()                                                                                    
    finally:                                                                                                        
        print "Available Threads Needed To Finish"                                                                  
        for t in threading.enumerate():                                                                             
        print "Thread: ",t.getName                                                                              

        print "Attempt to join threads to ensure all threads are finished"                                          
        for t in threading.enumerate():                                                                             
            print "About To Join : ",t.getName                                                                      
            if t not in initialThreads:                                                                         
                t.join()                                                                                        
    print "All Done"          
person joey.enfield    schedule 25.04.2013
comment
Была именно эта проблема с модульным тестированием в Odoo 8 (OpenERP). Когда он останавливается, он пытается присоединиться ко всем потокам и зависает на каком-то дополнительном потоке, добавленном Eclipse. В моем случае это был один ServerComm. Я добавил ваше дополнительное условие, и теперь оно работает нормально. Огромное спасибо :) - person Jerther; 27.02.2017
comment
Та же проблема с Odoo 10. Вот вопрос об этом, который я отправил в проект PyDev Debugger на github: github.com/fabioz/PyDev.Debugger/issues/84 - person Jerther; 24.04.2017