Python Popen()失败,并带有“;[WinError 6]句柄无效";有时在_cleanup()中

Python Popen()失败,并带有“;[WinError 6]句柄无效";有时在_cleanup()中,python,windows,python-3.x,popen,Python,Windows,Python 3.x,Popen,我在做一个django项目。其中一个视图调用Popen()。大多数情况下,一切正常。但偶尔Popen()会失败,并显示以下错误消息: Traceback (most recent call last): File "C:\***\views.py", line 116, in process_request proc = Popen(['python', exec_path, input_excel, current_tab, project]) File "C:\Python3

我在做一个django项目。其中一个视图调用Popen()。大多数情况下,一切正常。但偶尔Popen()会失败,并显示以下错误消息:

Traceback (most recent call last):
  File "C:\***\views.py", line 116, in process_request
    proc = Popen(['python', exec_path, input_excel, current_tab, project])
  File "C:\Python34\lib\subprocess.py", line 754, in __init__
    _cleanup()
  File "C:\Python34\lib\subprocess.py", line 474, in _cleanup
    res = inst._internal_poll(_deadstate=sys.maxsize)
  File "C:\Python34\lib\subprocess.py", line 1146, in _internal_poll
    if _WaitForSingleObject(self._handle, 0) == _WAIT_OBJECT_0:
OSError: [WinError 6] The handle is invalid
重新启动服务器通常可以解决问题,但稍后可能会再次出现。失败后立即进行的尝试通常也会失败(通过循环实现)。多次手动重新加载页面有时会解决此问题

我还尝试了64位和32位python版本。这两方面都有问题

似乎_cleanup()管理用于避免僵尸进程的_活动列表。考虑到我使用的是windows,我注释掉了Popen()中的_cleanup(),到目前为止它似乎工作得很好。显然这不是一个合适的解决办法。还有更好的主意吗

更新:

按照埃里克森的建议,我仔细观察了把手。由于某种原因,django的autoreload.py似乎关闭了进程句柄。详情见下文

---------------------------------------------------
try handle:

Handle(908)
  File "manage.py", line 10, in <module>
    execute_from_command_line(sys.argv)
  File "C:\Python34\lib\site-packages\django\core\management\__init__.py", line 338, in execute_from_command_line
    utility.execute()
  File "C:\Python34\lib\site-packages\django\core\management\__init__.py", line 330, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "C:\Python34\lib\site-packages\django\core\management\base.py", line 393, in run_from_argv
    self.execute(*args, **cmd_options)
  File "C:\Python34\lib\site-packages\django\core\management\commands\runserver.py", line 49, in execute
    super(Command, self).execute(*args, **options)
  File "C:\Python34\lib\site-packages\django\core\management\base.py", line 444, in execute
    output = self.handle(*args, **options)
  File "C:\Python34\lib\site-packages\django\core\management\commands\runserver.py", line 88, in handle
    self.run(**options)
  File "C:\Python34\lib\site-packages\django\core\management\commands\runserver.py", line 97, in run
    autoreload.main(self.inner_run, None, options)
  File "C:\Python34\lib\site-packages\django\utils\autoreload.py", line 325, in main
    reloader(wrapped_main_func, args, kwargs)
  File "C:\Python34\lib\site-packages\django\utils\autoreload.py", line 291, in python_reloader
    reloader_thread()
  File "C:\Python34\lib\site-packages\django\utils\autoreload.py", line 267, in reloader_thread
    change = fn()
  File "C:\Python34\lib\site-packages\django\utils\autoreload.py", line 204, in code_changed
    for filename in gen_filenames():
  File "C:\Python34\lib\site-packages\django\utils\autoreload.py", line 92, in gen_filenames
    _cached_filenames = clean_files(_cached_filenames)
  File "C:\Python34\lib\site-packages\django\utils\autoreload.py", line 139, in clean_files
    if os.path.exists(filename):
  File "C:\Python34\lib\genericpath.py", line 19, in exists
    os.stat(path)
  File "C:\Python34\lib\subprocess.py", line 452, in Close
    print(traceback.print_stack())
None
handle closed.
---------------------------------------------------
后来异常抱怨句柄(908)。
我不太明白os.stat(path)是如何关闭句柄的,以及为什么subprocess.py没有将进程从活动列表中删除。

如果进程在调用
Popen时仍在运行,那么它会被添加到
\u active
列表中,以便
\u cleanu
以后轮询它。子流程模块中的任何代码都不应关闭流程句柄。我怀疑其他一些代码正在调用
CloseHandle
,这可能是由于以前关闭的句柄(例如文件句柄)的句柄值被重新用于子进程。某些代码可能错误地关闭了子进程的句柄。这是有道理的。但有一件事我还是很困惑。为什么句柄之后又变为有效。我注意到,通常在进程完成后,cleanup()会正常运行,而不会抱怨句柄无效。即使相同的句柄以前导致了问题。句柄是每个进程表的索引,其中包含指向内核对象的指针。关闭句柄后,表中相应的插槽和句柄值可供重用,例如用于
subprocess.Popen使用的进程句柄。我在问题中更新了我认为关闭句柄的原因。但是我仍然不知道如何解决这个问题。考虑到涉及
Popen
实例的引用周期,调用堆栈可能是由于异步垃圾收集造成的。但这确实表明,
Popen
实例正在完成,它完成/关闭引用的
句柄。如果
\u active
列表中引用了
Popen
实例,则不会发生这种情况。我需要做更多的工作来检测事件序列——调用跟踪、日志记录、断点(Python pdb和Windows windbg)。我想我更愿意切换到总是调用
wait
方法来避免这个问题。如果在调用
Popen.\uu del\uuu
时进程仍在运行,它会被添加到
\u active
列表中,以便
\u cleanu
以后轮询它。子流程模块中的任何代码都不应关闭流程句柄。我怀疑其他一些代码正在调用
CloseHandle
,这可能是由于以前关闭的句柄(例如文件句柄)的句柄值被重新用于子进程。某些代码可能错误地关闭了子进程的句柄。这是有道理的。但有一件事我还是很困惑。为什么句柄之后又变为有效。我注意到,通常在进程完成后,cleanup()会正常运行,而不会抱怨句柄无效。即使相同的句柄以前导致了问题。句柄是每个进程表的索引,其中包含指向内核对象的指针。关闭句柄后,表中相应的插槽和句柄值可供重用,例如用于
subprocess.Popen使用的进程句柄。我在问题中更新了我认为关闭句柄的原因。但是我仍然不知道如何解决这个问题。考虑到涉及
Popen
实例的引用周期,调用堆栈可能是由于异步垃圾收集造成的。但这确实表明,
Popen
实例正在完成,它完成/关闭引用的
句柄。如果
\u active
列表中引用了
Popen
实例,则不会发生这种情况。我需要做更多的工作来检测事件序列——调用跟踪、日志记录、断点(Python pdb和Windows windbg)。我想我宁愿切换到总是调用
wait
方法来避免这个问题。
def Close(self, CloseHandle=_winapi.CloseHandle):
            print ('---------------------------------------------------')
            print ('try handle:')
            print (self)
            if not self.closed:
                self.closed = True
                CloseHandle(self)
                print(traceback.print_stack())
                print ('handle closed.')
            print ('---------------------------------------------------')