У меня возникла проблема при использовании 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"