ฉันชอบคำตอบของ zzzeek แต่ Andre ถูกต้องว่าต้องมีคิวเพื่อป้องกันการสับสน ฉันโชคดีไปกับท่อ แต่ก็เห็นการผสมปนเปซึ่งคาดว่าค่อนข้าง การปรับใช้นั้นยากกว่าที่ฉันคิดโดยเฉพาะอย่างยิ่งเนื่องจากการทำงานบน Windows ซึ่งมีข้อ จำกัด เพิ่มเติมบางประการเกี่ยวกับตัวแปรและสิ่งของทั่วโลก (ดู: Python การประมวลผลมัลติโพรเซสซิงติดตั้งบน Windows เป็นอย่างไร )
แต่ในที่สุดฉันก็ทำให้มันใช้งานได้ ตัวอย่างนี้อาจไม่สมบูรณ์ดังนั้นยินดีรับความคิดเห็นและคำแนะนำ นอกจากนี้ยังไม่รองรับการตั้งค่าตัวจัดรูปแบบหรือสิ่งอื่นนอกเหนือจากตัวบันทึกรูท โดยทั่วไปคุณต้องเริ่มต้นตัวบันทึกใหม่ในกระบวนการพูลแต่ละรายการด้วยคิวและตั้งค่าคุณลักษณะอื่น ๆ บนตัวบันทึก
อีกครั้งคำแนะนำใด ๆ เกี่ยวกับวิธีการทำให้โค้ดดีขึ้นยินดีต้อนรับ แน่นอนว่าฉันยังไม่รู้จักกลอุบาย Python ทั้งหมด :-)
import multiprocessing, logging, sys, re, os, StringIO, threading, time, Queue
class MultiProcessingLogHandler(logging.Handler):
def __init__(self, handler, queue, child=False):
logging.Handler.__init__(self)
self._handler = handler
self.queue = queue
# we only want one of the loggers to be pulling from the queue.
# If there is a way to do this without needing to be passed this
# information, that would be great!
if child == False:
self.shutdown = False
self.polltime = 1
t = threading.Thread(target=self.receive)
t.daemon = True
t.start()
def setFormatter(self, fmt):
logging.Handler.setFormatter(self, fmt)
self._handler.setFormatter(fmt)
def receive(self):
#print "receive on"
while (self.shutdown == False) or (self.queue.empty() == False):
# so we block for a short period of time so that we can
# check for the shutdown cases.
try:
record = self.queue.get(True, self.polltime)
self._handler.emit(record)
except Queue.Empty, e:
pass
def send(self, s):
# send just puts it in the queue for the server to retrieve
self.queue.put(s)
def _format_record(self, record):
ei = record.exc_info
if ei:
dummy = self.format(record) # just to get traceback text into record.exc_text
record.exc_info = None # to avoid Unpickleable error
return record
def emit(self, record):
try:
s = self._format_record(record)
self.send(s)
except (KeyboardInterrupt, SystemExit):
raise
except:
self.handleError(record)
def close(self):
time.sleep(self.polltime+1) # give some time for messages to enter the queue.
self.shutdown = True
time.sleep(self.polltime+1) # give some time for the server to time out and see the shutdown
def __del__(self):
self.close() # hopefully this aids in orderly shutdown when things are going poorly.
def f(x):
# just a logging command...
logging.critical('function number: ' + str(x))
# to make some calls take longer than others, so the output is "jumbled" as real MP programs are.
time.sleep(x % 3)
def initPool(queue, level):
"""
This causes the logging module to be initialized with the necessary info
in pool threads to work correctly.
"""
logging.getLogger('').addHandler(MultiProcessingLogHandler(logging.StreamHandler(), queue, child=True))
logging.getLogger('').setLevel(level)
if __name__ == '__main__':
stream = StringIO.StringIO()
logQueue = multiprocessing.Queue(100)
handler= MultiProcessingLogHandler(logging.StreamHandler(stream), logQueue)
logging.getLogger('').addHandler(handler)
logging.getLogger('').setLevel(logging.DEBUG)
logging.debug('starting main')
# when bulding the pool on a Windows machine we also have to init the logger in all the instances with the queue and the level of logging.
pool = multiprocessing.Pool(processes=10, initializer=initPool, initargs=[logQueue, logging.getLogger('').getEffectiveLevel()] ) # start worker processes
pool.map(f, range(0,50))
pool.close()
logging.debug('done')
logging.shutdown()
print "stream output is:"
print stream.getvalue()