os.listdir在内部执行什么系统调用,并且由于os.listdir
在已安装的网络驱动器上的情况,是否有可能挂起Python进程?
我们怀疑我们的应用服务器存在问题,因为os.listdir
试图列出安装在linux机器上的samba共享。显然,在我们遇到这个问题的时候,samba共享的DNS已经发生了变化。我们仍在尝试复制这种情况,但任何人都可以告诉我它将如何运作?像ls
这样的命令也会像这样挂起来吗?
我们有什么方法可以在用户空间处理这个问题吗?
答案 0 :(得分:5)
CPython的implementation of 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
在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