Могу ли я войти с именами / идентификаторами потоков в методы `__del__`? - PullRequest
0 голосов
/ 09 ноября 2018

В библиотеке Python stdlib logging с помощью макроса %(thread) или %(threadName) ( здесь указано ) вызывается threading.current_thread ( code ), который вынимает threading - внутренний замок.

Это, в соответствии с предупреждениями в __del__ документации , вызывает проблему, если вы хотите войти в систему __del__. Мы столкнулись с однопоточной проблемой взаимоблокировки , вызванной этим.

Итак, мой вопрос: есть ли всегда безопасный способ входа изнутри __del__ методы ? Было бы неплохо зарегистрировать информацию о том, что в нашем случае соединения закрываются из-за того, что их владельцы являются GC'd, но это может вызвать проблемы в (возможно, маловероятном) случае, когда пользователь включает отладочную регистрацию и добавляет информацию о потоке к их конфигурации отладки. Я нашел несколько сообщений о более явном совместном использовании ресурсов в __del__, но ни одного о поведении ведения журнала stdlib.

1 Ответ

0 голосов
/ 09 ноября 2018

CPython stdlib версия из threading.current_thread() фактически не принимает блокировку. Проблема, с которой вы столкнулись, имеет отношение к eventlet, который делает кучу патчей, чтобы связываться с системой потоков. Хотя одним из подходов может быть прекращение использования eventlet, для этого, вероятно, потребуется переписать все приложение целиком, и это не исправит ни один из других способов, с помощью которых вы можете попытаться получить блокировку во время регистрации - например, если __str__ метод оказывается нужным для блокировки.

Самым близким к безопасному способу входа в систему __del__ или выполнения какой-либо сложной работы в __del__, вероятно, является вместо этого __del__ отправить сообщение, сообщающее некоторому другому коду для ведения журнала вместо этого. Это привело бы к задержке между __del__ и фактическим ведением журнала, но такая задержка, по сути, неизбежна, поскольку мы должны задерживать ведение журнала до тех пор, пока необходимые ему ресурсы не будут доступны. Это также не гарантирует, что вызов регистрации и __del__ происходят в одном потоке; в контексте не-eventlet, вероятно, безопасно вызывать current_thread(), чтобы выяснить, какой поток обрабатывает __del__, но с помощью eventlet, вероятно, нет хорошего способа.

У большинства способов отправки сообщения были бы аналогичные проблемы безопасности потока или повторного входа, но Python 3.7 добавляет класс queue.SimpleQueue с методом put, предназначенным для повторного входа. Использование его для управления __del__ сообщениями журнала может выглядеть примерно так:

import queue
del_log_queue = queue.SimpleQueue()

def stop_del_logging():
    del_log_queue.put(None)

...

def __del__(self):
    del_log_queue.put((tuple, of, relevant, information))

...

# in some other thread

while True:
    info = del_log_queue.get()
    if info is None:
        break
    relevant_logger.log(do_something_with(info))

В контексте, не связанном с Eventlet, может быть безопасно иметь logging.QueueHandler и logging.QueueListener для обработки SimpleQueue, но с eventlet это не сработает, потому что нам нужно отложить создание LogRecord, пока у нас не выйдет __del__.

...