Мне удалось постоянно воспроизвести поведение с:
- Python 3.7.6 ( pc064 ( 64bit ), затем также с pc032 )
- PyGraphviz 1.5 (который я создал - доступен для загрузки на [GitHub]: CristiFati / Prebuilt- Двоичные файлы - различное программное обеспечение, созданное на различных платформах. (под PyGraphviz , естественно) - может также потребоваться проверка [SO]: установка pygraphviz на Windows 10 64-бит , Python 3,6 (@ ответ CristiFati) )
- Graphviz 2.42.2 (( pc032 ) аналогично # 2 . )
Я подозревал неопределенное поведение ( UB ) где-то в коде, даже если поведение было точно то же самое:
- ОК для 169 графики
- Cra sh для 170
Сделал некоторую отладку (добавил немного print (f) * 10 64 * операторов в agraph.py и cgraph.dll ( write. c)).
PyGraphviz вызывает Graphviz инструментов ( .exe s) для многих операций. Для этого он использует subprocess.Popen и связывается с дочерним процессом через 3 доступных потока ( stdin , stdout , stderr ) .
С самого начала я заметил, что 170 * 3 = 510
(очень близко к 512 ( 0x200 )), но не обратил столько внимания, сколько следовало бы до позже (в основном потому, что процесс Python (с кодом ниже) имел не более ~ 150 открытых дескрипторов в диспетчере задач ( TM ), а также Process Explorer ( PE ) ).
Однако, немного Google ing обнаружено:
Ниже приведен ваш код, который я изменил для отладки и воспроизведения ошибки. Для этого требуется пакет PyWin32 (python -m pip install pywin32
).
code00.py :
#!/usr/bin/env python
import sys
import os
#import time
import pygraphviz as pgv
import win32file as wfile
def handle_graph(idx, dir_name):
graph_name = "draw_{0:03d}".format(idx)
graph_args = {
"name": graph_name,
"strict": False,
"directed": False,
"compound": True,
"ranksep": "0.2",
"nodesep": "0.2",
}
graph = pgv.AGraph(**graph_args)
# Draw Graph
img_base_name = graph_name + ".png"
print(" {0:s}".format(img_base_name))
graph.layout(prog="dot")
img_full_name = os.path.join(dir_name, img_base_name)
graph.draw(img_full_name)
graph.close() # !!! Has NO (visible) effect, but I think it should be called anyway !!!
def main(*argv):
print("OLD max open files: {0:d}".format(wfile._getmaxstdio()))
# 513 is enough for your original code (170 graphs), but you can set it up to 8192
wfile._setmaxstdio(513) # !!! COMMENT this line to reproduce the crash !!!
print("NEW max open files: {0:d}".format(wfile._getmaxstdio()))
dir_name = "Graph"
# Create Directory
if not os.path.isdir(dir_name):
os.makedirs(dir_name)
#ts_global_start = time.time()
start = 0
count = 169
#count = 1
step_sleep = 0.05
for i in range(start, start + count):
#ts_local_start = time.time()
handle_graph(i, dir_name)
#print(" Time: {0:.3f}".format(time.time() - ts_local_start))
#time.sleep(step_sleep)
handle_graph(count, dir_name)
#print("Global time: {0:.3f}".format(time.time() - ts_global_start - step_sleep * count))
if __name__ == "__main__":
print("Python {0:s} {1:d}bit on {2:s}\n".format(" ".join(item.strip() for item in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform))
main(*sys.argv[1:])
print("\nDone.")
Вывод :
e:\Work\Dev\StackOverflow\q060876623>"e:\Work\Dev\VEnvs\py_pc064_03.07.06_test0\Scripts\python.exe" code00.py
Python 3.7.6 (tags/v3.7.6:43364a7ae0, Dec 19 2019, 00:42:30) [MSC v.1916 64 bit (AMD64)] 64bit on win32
OLD max open files: 512
NEW max open files: 513
draw_000.png
draw_001.png
draw_002.png
...
draw_167.png
draw_168.png
draw_169.png
Done.
Выводы :
- Очевидно, некоторые файловые дескрипторы ( fd s) открыты, хотя они не «видны» TM или PE (вероятно, они находятся на более низком уровне). Однако я не знаю, почему это происходит (это ошибка MS UCRT ?), Но, с моей точки зрения, после завершения дочернего процесса его потоки должны быть закрыты, но я не знаю как заставить его ( это было бы правильным исправлением )
- Кроме того, поведение ( cra sh) при попытке записи ( не открыто ) для fd (выше предела), кажется немного странным
- В качестве обходного пути, max open FD S число может быть увеличено. Основываясь на следующем неравенстве:
3 * (graph_count + 1) <= max_fds
, вы можете получить представление о числах. Оттуда, если вы установите ограничение на 8192 (я не проверял это), вы сможете обрабатывать 2729 графиков (при условии, что нет никаких дополнительных fd s открывается кодом)
Примечания :