带有守护线程的单例垃圾收集器
Garbage collector of singleton with daemon thread
我已经将记录器实现为单例,其中所有消息都进入队列,守护线程从队列中收集这些消息并打印出来。
我使用守护线程的原因是我不必在完成记录器(或应用程序退出)后显式关闭记录器。
我预计一旦应用程序关闭时记录器被删除(由垃圾收集器),然后 __del__
方法将 运行 并在之后清理。我很惊讶事实并非如此。
当我将线程更改为非守护进程时,它工作得很好(显然我必须进行一些其他更改以便应用程序退出)。我想知道我是否做错了什么,或者这只是一个糟糕的做法。
附上代码:(我建议 __del__
函数之后的所有内容都不有趣)。
import os
import sys
import time
import Queue
import weakref
import datetime
import threading
class Logger(object):
"""
Logger class implemented with a queue of messages, and supports only a single instace.
This instance can be acquired by using the "GetLogger" method.
"""
__instance = None
@classmethod
def GetLogger(cls, fpath, source_name=None):
if cls.__instance is None:
return Logger(fpath, source_name=source_name)
else:
if source_name is None:
cls.__instance().log('%s@%s: %s\n' % (cls.current_date(), cls.current_time(), "Using existing Logger instance"), "REUSAGE")
else:
cls.__instance().log('%s@%s - %-17s: %s\n' % (cls.current_date(), cls.current_time(), source_name, "Using existing Logger instance"), "REUSAGE")
return cls.__instance()
def __init__(self, fpath, start_time = time.time(), source_name=None):
if self.__instance is not None:
raise ValueError("Singleton object already exists")
self.__instance = weakref.ref(self)
self.__start_time = start_time
self.__queue = Queue.Queue()
self.__listener = threading.Thread(target=self._listen)
self.__listener.daemon = True
self.__listener.start()
if not os.path.exists(os.path.dirname(fpath)):
os.makedirs(os.path.dirname(fpath))
self.__f = open(fpath, 'a')
if source_name is None:
self.log('%s@%s: %s\n' % (self.current_date(), self.current_time(), "Created a new Logger instance"), "CREATION")
else:
self.log('%s@%s - %-17s: %s\n' % (self.current_date(), self.current_time(), source_name, "Created a new Logger instance"), "CREATION")
@staticmethod
def current_date():
return str(datetime.datetime.now().date().isoformat())
@staticmethod
def current_time():
return str(datetime.datetime.now().time().isoformat())
def _listen(self):
while True:
msg = self.__queue.get()
if msg is None:
break
self.__print_message(msg)
def __print_message(self, msg_tup): # msg_tup = (message, level, stdout)
msg_time = time.time()
if msg_tup[2]:
try:
print(("|%013.6f|%-8s>>>%s" % (msg_time - self.__start_time, msg_tup[1], msg_tup[0]))),
sys.stdout.flush()
except:
pass
try:
self.__f.write("|%013.6f|%-8s>>>%s" % (msg_time - self.__start_time, msg_tup[1], msg_tup[0]))
self.__f.flush()
except:
pass
def log(self, msg, level, to_stdout=True):
self.__queue.put((msg, level, to_stdout))
def close(self):
self.__queue.put(None)
self.__instance = None
def __del__(self):
while not self.__queue.empty():
msg = self.__queue.get()
if msg is not None:
self.__print_message(msg)
print("Dead...")
self.__f.close()
self.close()
def info(self, msg, source_name=None, to_stdout=True):
if source_name is None:
self.log('%s@%s: %s\n' % (self.current_date(), self.current_time(), msg), "INFO", to_stdout=to_stdout)
else:
self.log('%s@%s - %-17s: %s\n' % (self.current_date(), self.current_time(), source_name, msg), "INFO", to_stdout=to_stdout)
def debug(self, msg, source_name=None, to_stdout=True):
if source_name is None:
self.log('%s@%s: %s\n' %
(self.current_date(), self.current_time(), msg), "DEBUG", to_stdout=to_stdout)
else:
self.log('%s@%s - %-17s: %s\n' % (self.current_date(), self.current_time(), source_name, msg), "DEBUG", to_stdout=to_stdout)
def trace(self, msg, sdource_name=None, to_stdout=True):
if source_name is None:
self.log('%s@%s: %s\n' %
(self.current_date(), self.current_time(), msg), "TRACE", to_stdout=to_stdout)
else:
self.log('%s@%s - %-17s: %s\n' % (self.current_date(), self.current_time(), source_name, msg), "TRACE", to_stdout=to_stdout)
def warn(self, msg, source_name=None, to_stdout=True):
if source_name is None:
self.log('%s@%s: %s\n' %
(self.current_date(), self.current_time(), msg), "WARN", to_stdout=to_stdout)
else:
self.log('%s@%s - %-17s: %s\n' % (self.current_date(), self.current_time(), source_name, msg), "WARN", to_stdout=to_stdout)
def error(self, msg, source_name=None, to_stdout=True):
if source_name is None:
self.log('%s@%s: %s\n' %
(self.current_date(), self.current_time(), msg), "ERROR", to_stdout=to_stdout)
else:
self.log('%s@%s - %-17s: %s\n' % (self.current_date(), self.current_time(), source_name, msg), "ERROR", to_stdout=to_stdout)
def critical(self, msg, source_name=None, to_stdout=True):
if source_name is None:
self.log('%s@%s: %s\n' %
(self.current_date(), self.current_time(), msg), "CRITICAL", to_stdout=to_stdout)
else:
self.log('%s@%s - %-17s: %s\n' % (self.current_date(), self.current_time(), source_name, msg), "CRITICAL", to_stdout=to_stdout)
GC 永远不会回收您的 Logger
实例,只要程序中的任何 "live" 变量仍然包含对它的引用。 listen(self):...
方法的 self
参数就是这样一个变量,这是守护线程是 运行.
的顶级方法
def _listen(self):
while True:
msg = self.__queue.get()
if msg is None:
break
self.__print_message(msg)
GC 无法回收 Logger
实例,直到守护线程 return 来自 _listen()
。只有一种方法可以实现:
def close(self):
self.__queue.put(None)
如果您 "close" 您的记录器,那么守护线程最终将从队列中获取 None
,它将 return 来自 _listen()
调用,并且守护线程线程将结束。但是,你说
The reason I used a daemon thread is so I don't have to explicitly close the logger.
如果不关闭记录器,守护线程将永远不会结束,_listen(self)
中的 self
arg 永远不会超出范围,Logger
实例永远不会被回收。
我已经将记录器实现为单例,其中所有消息都进入队列,守护线程从队列中收集这些消息并打印出来。
我使用守护线程的原因是我不必在完成记录器(或应用程序退出)后显式关闭记录器。
我预计一旦应用程序关闭时记录器被删除(由垃圾收集器),然后 __del__
方法将 运行 并在之后清理。我很惊讶事实并非如此。
当我将线程更改为非守护进程时,它工作得很好(显然我必须进行一些其他更改以便应用程序退出)。我想知道我是否做错了什么,或者这只是一个糟糕的做法。
附上代码:(我建议 __del__
函数之后的所有内容都不有趣)。
import os
import sys
import time
import Queue
import weakref
import datetime
import threading
class Logger(object):
"""
Logger class implemented with a queue of messages, and supports only a single instace.
This instance can be acquired by using the "GetLogger" method.
"""
__instance = None
@classmethod
def GetLogger(cls, fpath, source_name=None):
if cls.__instance is None:
return Logger(fpath, source_name=source_name)
else:
if source_name is None:
cls.__instance().log('%s@%s: %s\n' % (cls.current_date(), cls.current_time(), "Using existing Logger instance"), "REUSAGE")
else:
cls.__instance().log('%s@%s - %-17s: %s\n' % (cls.current_date(), cls.current_time(), source_name, "Using existing Logger instance"), "REUSAGE")
return cls.__instance()
def __init__(self, fpath, start_time = time.time(), source_name=None):
if self.__instance is not None:
raise ValueError("Singleton object already exists")
self.__instance = weakref.ref(self)
self.__start_time = start_time
self.__queue = Queue.Queue()
self.__listener = threading.Thread(target=self._listen)
self.__listener.daemon = True
self.__listener.start()
if not os.path.exists(os.path.dirname(fpath)):
os.makedirs(os.path.dirname(fpath))
self.__f = open(fpath, 'a')
if source_name is None:
self.log('%s@%s: %s\n' % (self.current_date(), self.current_time(), "Created a new Logger instance"), "CREATION")
else:
self.log('%s@%s - %-17s: %s\n' % (self.current_date(), self.current_time(), source_name, "Created a new Logger instance"), "CREATION")
@staticmethod
def current_date():
return str(datetime.datetime.now().date().isoformat())
@staticmethod
def current_time():
return str(datetime.datetime.now().time().isoformat())
def _listen(self):
while True:
msg = self.__queue.get()
if msg is None:
break
self.__print_message(msg)
def __print_message(self, msg_tup): # msg_tup = (message, level, stdout)
msg_time = time.time()
if msg_tup[2]:
try:
print(("|%013.6f|%-8s>>>%s" % (msg_time - self.__start_time, msg_tup[1], msg_tup[0]))),
sys.stdout.flush()
except:
pass
try:
self.__f.write("|%013.6f|%-8s>>>%s" % (msg_time - self.__start_time, msg_tup[1], msg_tup[0]))
self.__f.flush()
except:
pass
def log(self, msg, level, to_stdout=True):
self.__queue.put((msg, level, to_stdout))
def close(self):
self.__queue.put(None)
self.__instance = None
def __del__(self):
while not self.__queue.empty():
msg = self.__queue.get()
if msg is not None:
self.__print_message(msg)
print("Dead...")
self.__f.close()
self.close()
def info(self, msg, source_name=None, to_stdout=True):
if source_name is None:
self.log('%s@%s: %s\n' % (self.current_date(), self.current_time(), msg), "INFO", to_stdout=to_stdout)
else:
self.log('%s@%s - %-17s: %s\n' % (self.current_date(), self.current_time(), source_name, msg), "INFO", to_stdout=to_stdout)
def debug(self, msg, source_name=None, to_stdout=True):
if source_name is None:
self.log('%s@%s: %s\n' %
(self.current_date(), self.current_time(), msg), "DEBUG", to_stdout=to_stdout)
else:
self.log('%s@%s - %-17s: %s\n' % (self.current_date(), self.current_time(), source_name, msg), "DEBUG", to_stdout=to_stdout)
def trace(self, msg, sdource_name=None, to_stdout=True):
if source_name is None:
self.log('%s@%s: %s\n' %
(self.current_date(), self.current_time(), msg), "TRACE", to_stdout=to_stdout)
else:
self.log('%s@%s - %-17s: %s\n' % (self.current_date(), self.current_time(), source_name, msg), "TRACE", to_stdout=to_stdout)
def warn(self, msg, source_name=None, to_stdout=True):
if source_name is None:
self.log('%s@%s: %s\n' %
(self.current_date(), self.current_time(), msg), "WARN", to_stdout=to_stdout)
else:
self.log('%s@%s - %-17s: %s\n' % (self.current_date(), self.current_time(), source_name, msg), "WARN", to_stdout=to_stdout)
def error(self, msg, source_name=None, to_stdout=True):
if source_name is None:
self.log('%s@%s: %s\n' %
(self.current_date(), self.current_time(), msg), "ERROR", to_stdout=to_stdout)
else:
self.log('%s@%s - %-17s: %s\n' % (self.current_date(), self.current_time(), source_name, msg), "ERROR", to_stdout=to_stdout)
def critical(self, msg, source_name=None, to_stdout=True):
if source_name is None:
self.log('%s@%s: %s\n' %
(self.current_date(), self.current_time(), msg), "CRITICAL", to_stdout=to_stdout)
else:
self.log('%s@%s - %-17s: %s\n' % (self.current_date(), self.current_time(), source_name, msg), "CRITICAL", to_stdout=to_stdout)
GC 永远不会回收您的 Logger
实例,只要程序中的任何 "live" 变量仍然包含对它的引用。 listen(self):...
方法的 self
参数就是这样一个变量,这是守护线程是 运行.
def _listen(self):
while True:
msg = self.__queue.get()
if msg is None:
break
self.__print_message(msg)
GC 无法回收 Logger
实例,直到守护线程 return 来自 _listen()
。只有一种方法可以实现:
def close(self):
self.__queue.put(None)
如果您 "close" 您的记录器,那么守护线程最终将从队列中获取 None
,它将 return 来自 _listen()
调用,并且守护线程线程将结束。但是,你说
The reason I used a daemon thread is so I don't have to explicitly close the logger.
如果不关闭记录器,守护线程将永远不会结束,_listen(self)
中的 self
arg 永远不会超出范围,Logger
实例永远不会被回收。