print
в Python не ориентировано на многопотоковое исполнение согласно они статьи.
Обходное решение Python 3 предлагается в последней статье.
Как я получаю ориентированное на многопотоковое исполнение print
в Python 2.6?
Интересная проблема - учитывая все, что происходит в операторе print
, включая настройку и проверку атрибута softtspace
, делая это " threadsafe "(что на самом деле означает: поток, который печатает, только передает" контроль стандартного вывода "другому потоку, когда он печатает новую строку, так что каждая целая выводимая строка гарантированно поступает из одного потока) была немного сложной задачей (обычный простой подход к фактической потоковой безопасности - делегирование отдельному потоку исключительно «владеть» и обрабатывать sys.stdout
, связываться с ним через Queue.Queue - не все это полезно, поскольку проблема заключается в не потокобезопасности [[даже с простой печатью
нет риска сбоя, а символы, которые попадают в стандартный вывод, - это именно те символы, которые получают напечатано]], но необходимость взаимного исключения потоков для расширенного диапазона операций) .
Думаю, я сделал это ...:
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...
Эффект "сериализации" (при котором потоки кажутся "красиво округленными"). robin ", как указано выше) является побочным эффектом того факта, что поток, который становится текущим печатаемым, значительно медленнее, чем другие (все они ждут! -). Комментируя 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...
, т.е. более типичный «многопоточный вывод» ... за исключением гарантии, что каждая строка вывода полностью принадлежит одному потоку.
Конечно, поток, который, например, печатает 'ciao',
, сохранит «владение» стандартным выводом до тех пор, пока он, наконец, не выполнит печать без запятой, и другие потоки желающие распечатать могут спать довольно долгое время (как еще можно гарантировать, что каждая строка в выводе поступает из одного потока? ну, одна архитектура должна была бы накапливать частичные строки для локального хранилища потоков вместо того, чтобы фактически записывать их в стандартный вывод, и делайте запись только при получении \ n
... деликатно для правильного чередования с настройками softspace
, я боюсь, но, вероятно, выполнимо).
Проблема в том, что python использует отдельные коды операций для печати NEWLINE и печати самого объекта. Самое простое решение - просто использовать явный sys.stdout.write с явным переводом строки.
Я не знаю, есть ли лучший способ вместо этого механизма блокировки, но, по крайней мере, это выглядит просто. Я также не уверен, что печать действительно не является потокобезопасной.
Edit: Okay tested it my self now, you are right, you can get really wierdly looking output. И вам не нужен импорт future, он просто есть, потому что я использую 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')