Ответ 1
Интересная проблема - учитывая все то, что происходит в инструкции 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',
, будет сохранять "собственность" стандартного вывода, пока он, наконец, не выполнит печать без конечной запятой, а другие потоки, которые хотят печатать, могут долгое время спать (как else может гарантировать, что каждая строка на выходе поступает из одного потока? Ну, одна архитектура будет накапливать частичные линии для потоковой локализации, а не записывать их на стандартный вывод, и записывать только при получении \n
... нежный, чтобы правильно чередовать с настройками softspace
, боюсь, но, вероятно, возможно).