如何在Python 2.6中获得线程安全打印?
根据这些 文章,在Python中print
不是线程安全的。
后面的文章提供了Python 3的变通方法。
如何在Python 2.6中获得线程安全print
?
有趣的问题 – 考虑到在print
语句中发生的所有事情,包括softspace
属性的设置和检查,使其成为“线程安全”(实际上意味着:打印的线程只会产生“控制标准输出”到另一个线程当它打印一个换行符,这样输出的每一行都保证来自一个单独的线程)是一个挑战(通常的简单的方法来实际的线程安全 – 委派一个单独的线程完全“拥有”和处理sys.stdout
,通过Queue.Queue进行通信 – 并不是那么有用,因为这个问题不是线程安全的[即使使用普通print
,也不会有崩溃的风险,而标准输出上的字符是确切地说是打印的]],但是需要在线程之间相互排斥以扩大操作范围)。
所以,我觉得我做到了…:
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 == '\n': 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
评论), wait
的呼叫是为了保证混合输出。 包装,也就是说,上面的代码就像它看起来那样,并且(至less)Python 2.5及更高版本(我相信这也可能在早期版本中运行,但是我没有任何方便检查)输出是:
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
wait
,输出是相反的
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...
即更典型的“multithreading输出”…除了保证输出中的每行完全来自单个线程。
当然,例如print 'ciao',
的线程将保持标准输出的“所有权”,直到它最终确实执行没有尾随逗号的打印,而其他想要打印的线程可能会睡眠相当长的一段时间我们可以保证输出中的每一行都来自一个单独的线程吗?一个体系结构将会累积局部线程来线程化本地存储,而不是实际写入标准输出,而只能在接收到\n
进行写入。 ..微妙的交错与softspace
设置,我担心,但可能是可行的)。
问题是python使用独立的操作码来进行NEWLINE打印和打印对象本身。 最简单的解决scheme可能只是使用明确的换行符显式的sys.stdout.write。
我不知道是否有更好的方法,而不是这个locking机制,但至less看起来很容易。 我也不确定打印是否真的不是线程安全的。
编辑:好吧现在testing我的自我,你是对的,你可以得到真正奇怪的输出。 而且你不需要将来的导入,它只是在那里,因为我使用Python 2.7。
from __future__ import print_function from threading import Lock print_lock = Lock() def save_print(*args, **kwargs): with print_lock: print (*args, **kwargs) save_print("test", "omg", sep='lol')
通过实验,我发现以下作品很简单,适合我的需求:
print "your string here\n",
或者,在一个函数中,
def safe_print(content): print "{0}\n".format(content),
我的理解是,普通print
的隐式换行实际上是在单独的操作中输出到stdout,从而导致与其他print
操作的竞争状态。 通过删除这个隐含的换行符,并在string中包含换行符,我们可以避免这个问题。
2017编辑:这个答案开始吸取一些蒸汽,所以我只是想澄清。 这实际上并不确切地使print
“线程安全”。 如果print
彼此间隔几微秒,则输出的顺序可能会错误。 然而, 这样做是避免从并发线程执行的print
语句产生乱码输出,这是大多数人在问这个问题时真正想要的。
这里是一个testing,以显示我的意思是:
from concurrent.futures import ThreadPoolExecutor def normal_print(content): print content def safe_print(content): print "{0}\n".format(content), with ThreadPoolExecutor(max_workers=10) as executor: print "Normal Print:" for i in range(10): executor.submit(normal_print, i) print "---" with ThreadPoolExecutor(max_workers=10) as executor: print "Safe Print:" for i in range(10): executor.submit(safe_print, i)
输出:
Normal Print: 0 1 23 4 65 7 9 8 ---- Safe Print: 1 0 3 2 4 5 6 7 8 9