Интересная проблема - учитывая все, что происходит в операторе 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
предназначены для гарантии хаотически смешанного вывода в отсутствие этой гарантии взаимного исключения (откуда комментарий). С упаковкой, т. Е. Приведенным выше кодом в точности так, как он выглядит там, и (по крайней мере) 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...
Эффект «сериализации» (в результате чего потоки выглядят «красиво круговыми», как указано выше) является побочным эффектом того факта, что поток, который становится текущим печатающим, серьезно медленнее, чем другие (все те ждут! -). Закомментируя 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...
т.е. более типичный "многопоточный вывод" ... за исключением гарантии того, что каждая строка в выводе полностью состоит из одного потока.
Конечно, поток, который, например, print 'ciao',
, будет сохранять "владение" стандартным выводом, пока он, наконец, не выполнит печать без запятой, и другие потоки, желающие напечатать, могут спать в течение некоторого времени (как еще можно гарантировать, что каждая строка в выводе поступает из одного потока?), одна архитектура должна была бы накапливать частичные строки для потокового локального хранилища вместо того, чтобы фактически записывать их в стандартный вывод, и выполнять только запись боюсь, что при получении \n
... деликатного для правильного чередования настроек softspace
, но возможно)).