os.listdir在内部执行什么系统调用,并且由于存在os.listdir通过已安装的网络驱动器的情况,是否有可能挂起Python进程?
我们怀疑我们的应用服务器存在问题,因为os.listdir它试图列出安装在linux机器上的samba共享.显然,在我们遇到这个问题的时候,samba共享的DNS已经发生了变化.我们仍在尝试复制这种情况,但任何人都可以告诉我它将如何运作?并且命令ls也会像这样挂起来吗?
我们有什么方法可以在用户空间处理这个问题吗?
CPython的实现os.listdir使用特定于平台的C库调用来读取目录的内容.在类Unix的平台上,它们是opendir(3)和readdir(3),并且在Windows上它使用FindFirstFile和FindNextFile.
这些调用在存在无法访问的网络文件系统时的行为将取决于操作系统.使用Linux或Windows时,他们肯定会挂起系统命令(如ls挂起)的情况.为了防止任意长时间的暂停,可以使用专用框架,例如asyncio和twisted,它们使用非阻塞IO.但是,使用这些框架可能会令人生畏,并且通常需要在整个应用程序中使用它们,并将整个程序用于事件驱动模型.
在网络文件系统存在的情况下,确保IO系统调用不会阻塞的更简单且有点初学者友好的方法是使用线程.例如,这是一个safe_listdir返回目录内容的函数,或者None如果调用花费的时间超过指定的超时:
import os, threading
def safe_listdir(directory, timeout):
contents = []
t = threading.Thread(target=lambda: contents.extend(os.listdir(directory)))
t.daemon = True # don't delay program's exit
t.start()
t.join(timeout)
if t.is_alive():
return None # timeout
return contents
Run Code Online (Sandbox Code Playgroud)
在Python 3中,可以使用优秀的concurrent.futures包.它不仅简化了实现,还会safe_listdir多次调用创建的线程数,并确保引发的异常os.listdir正确传播给调用者:
import os, concurrent.futures
pool = concurrent.futures.ThreadPoolExecutor()
def safe_listdir(directory, timeout):
future = pool.submit(os.listdir, directory)
try:
return future.result(timeout)
except concurrent.futures.TimeoutError:
return None # timeout
Run Code Online (Sandbox Code Playgroud)