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
的目的是在没有互斥保证的情况下保证混乱的输出(因此有注释)。使用封装,即上面的代码完全按照它看起来的样子,并且(至少)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
注释掉,输出结果变为: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...
即更典型的“多线程输出”...但保证输出中的每行都完全来自单个线程。
当然,执行 print 'ciao',
的线程会一直“拥有”标准输出,直到最终执行不带尾随逗号的打印操作,并且其他想要打印的线程可能会等待相当长的时间(否则如何保证输出中的每行来自单个线程?一个架构将部分行累积到线程本地存储中而不是实际写入标准输出,并且仅在收到 \n
后进行写入......我担心与 softspace
设置正确交错在一起,但可能可行)。
通过尝试,我发现以下方法可行,简单易行,并且符合我的需求:
print "your string here\n",
或者,包装在一个函数中:
def safe_print(content):
print "{0}\n".format(content),
我的理解是,普通print
语句的隐式换行实际上会在单独的操作中输出到标准输出,从而与其他print
操作产生竞争条件。通过使用额外的,
去除此隐式换行,并将换行符包含在字符串中,我们可以避免这个问题。
2020年编辑: 这是 Python 3 版本的代码(感谢评论区的 Bob Stein 提供灵感):
def safe_print(*args, sep=" ", end="", **kwargs):
joined_string = sep.join([ str(arg) for arg in args ])
print(joined_string + "\n", sep=sep, end=end, **kwargs)
print
来连接多个传入参数会导致输出信息混乱,所以我们需要自己来完成这个任务。
print
“线程安全”。如果print
之间相隔时间只有微秒级别,输出结果可能会错位。然而,这确实可以避免在并发线程中执行print
语句时出现输出混乱的情况,这也是大多数人在问这个问题时真正想要的结果。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
print("One string with no commas and its own LF\n", end="")
是 Python 3 版本的代码。对我来说,这两个标准使 print() 原子化。但是 print("Separate", "strings", "joined by commas\n", end="")
可能会被拆分。 - Bob Stein问题在于Python使用不同的操作码来打印NEWLINE和对象本身。最简单的解决方案可能是使用显式的sys.stdout.write和显式的换行符。
print
语句也会输出不稳定的换行符。您 必须 使用 sys.stdout.write(s + '\n')
来避免这种情况。 - efotinisfrom __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
不是线程安全的? - Ignacio Vazquez-Abrams