如何在 Python 2.6 中获得线程安全打印? [英] How do I get a thread safe print in Python 2.6?
问题描述
print
在 Python 中不是线程安全的a href="http://bramcohen.livejournal.com/70686.html" rel="noreferrer">文章.
print
in Python is not thread safe according to these articles.
后一篇文章中提供了 Python 3 解决方法.
A Python 3 work-around is offered in the latter article.
如何在 Python 2.6 中获得线程安全的print
?
How do I get a thread safe print
in Python 2.6?
推荐答案
有趣的问题 -- 考虑在 print
语句中发生的所有事情,包括 softspace 的设置和检查
属性,使其成为线程安全的"(意思是,实际上:一个正在打印的线程在打印换行符时只将标准输出的控制"交给另一个线程,因此输出的每一整行都保证来自单线程)是一个挑战(实际线程安全通常的简单方法——委托一个单独的线程专门拥有"和处理sys.stdout
,通信通过 Queue.Queue 到它——并不是那么有用,因为问题是不是线程安全[[即使使用普通的print
,也没有崩溃的风险和最终出现在标准输出上的字符正是那些被打印出来的字符]] 但是线程之间需要互斥以进行扩展操作).
Interesting problem -- considering all the things that happen within a print
statement, including the setting and checking of the softspace
attribute, making it "threadsafe" (meaning, actually: a thread that's printing only yields "control of standard output" to another thread when it's printing a newline, so that each entire line that's output is guaranteed to come from a single thread) was a bit of a challenge (the usual easy approach to actual thread safety -- delegating a separate thread to exclusively "own" and handle sys.stdout
, communicate to it via Queue.Queue -- isn't all that useful, since the problem is not thread safety [[even with a plain print
there is no risk of crashing and the characters that end up on standard output are exactly those which get printed]] but the need for mutual exclusion among threads for an extended range of operations).
所以,我想我做到了...:
So, I think I made it...:
import random
import sys
import thread
import threading
import time
def wait():
time.sleep(random.random())
return 'W'
def targ():
for n in range(8):
wait()
print 'Thr', wait(), thread.get_ident(), wait(), 'at', wait(), n
tls = threading.local()
class ThreadSafeFile(object):
def __init__(self, f):
self.f = f
self.lock = threading.RLock()
self.nesting = 0
def _getlock(self):
self.lock.acquire()
self.nesting += 1
def _droplock(self):
nesting = self.nesting
self.nesting = 0
for i in range(nesting):
self.lock.release()
def __getattr__(self, name):
if name == 'softspace':
return tls.softspace
else:
raise AttributeError(name)
def __setattr__(self, name, value):
if name == 'softspace':
tls.softspace = value
else:
return object.__setattr__(self, name, value)
def write(self, data):
self._getlock()
self.f.write(data)
if data == '
':
self._droplock()
# comment the following statement out to get guaranteed chaos;-)
sys.stdout = ThreadSafeFile(sys.stdout)
thrs = []
for i in range(8):
thrs.append(threading.Thread(target=targ))
print 'Starting'
for t in thrs:
t.start()
for t in thrs:
t.join()
print 'Done'
对 wait
的调用旨在保证在没有这种互斥保证的情况下混乱的混合输出(评论由此而来).使用包装,即上面的代码与它看起来完全一样,并且(至少)Python 2.5 及更高版本(我相信这也可以在早期版本中运行,但我没有任何很容易检查)输出是:
The calls to wait
are intended to guarantee chaotically mixed output in the absence of this mutual exclusion guarantee (whence the comment). With the wrapping, i.e., the above code exactly as it looks there, and (at least) Python 2.5 and up (I believe this may run in earlier versions, too, but I don't have any easily at hand to check) the output is:
Thr W -1340583936 W at W 0
Thr W -1340051456 W at W 0
Thr W -1338986496 W at W 0
Thr W -1341116416 W at W 0
Thr W -1337921536 W at W 0
Thr W -1341648896 W at W 0
Thr W -1338454016 W at W 0
Thr W -1339518976 W at W 0
Thr W -1340583936 W at W 1
Thr W -1340051456 W at W 1
Thr W -1338986496 W at W 1
...more of the same...
序列化"效应(线程看起来像上面那样很好地循环")是这样一个事实的副作用,即当前正在打印的线程比其他线程慢得多(所有那些等待!-).注释掉wait
中的time.sleep
,输出为
The "serialization" efect (whereby the threads appear to "nicely round-robin" as above) is a side effect of the fact that the thread that gets to be the currently-printing one is seriously slower than the others (all those waits!-). Commenting out the time.sleep
in wait
, the output is instead
Thr W -1341648896 W at W 0
Thr W -1341116416 W at W 0
Thr W -1341648896 W at W 1
Thr W -1340583936 W at W 0
Thr W -1340051456 W at W 0
Thr W -1341116416 W at W 1
Thr W -1341116416 W at W 2
Thr W -1338986496 W at W 0
...more of the same...
即更典型的多线程输出"……除了保证输出中的每一行完全来自一个线程.
i.e. a more typical "multithreaded output"... except for the guarantee that each line in the output comes entirely from one single thread.
当然,一个线程,例如,print 'ciao',
将保持标准输出的所有权",直到它最终执行没有尾随的打印逗号,而其他想要打印的线程可能会休眠一段时间(否则如何保证输出中的每一行都来自单个线程?嗯,一种架构是将部分行累积到线程本地存储中,而不是实际写入它们到标准输出,并且只在收到
时才进行写入......我担心,它很微妙,可以与 softspace
设置正确交错,但可能可行).
Of course, a thread that does, e.g., print 'ciao',
will keep "ownership" of standard output until it finally does perform a print without a trailing comma, and other threads wanting to print may sleep for quite a while (how else can one guarantee that each line in the output comes from a single thread? well, one architecture would be to accumulate partial lines to thread local storage instead of actually writing them to standard output, and only do the writing on receipt of the
... delicate to interleave properly with softspace
settings, I fear, but probably feasible).
这篇关于如何在 Python 2.6 中获得线程安全打印?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!