Как я получаю ориентированную на многопотоковое исполнение печать в Python 2.6?

print в Python не ориентировано на многопотоковое исполнение согласно они статьи.

Обходное решение Python 3 предлагается в последней статье.

Как я получаю ориентированное на многопотоковое исполнение print в Python 2.6?

49
задан knorv 12 June 2010 в 08:15
поделиться

3 ответа

Интересная проблема - учитывая все, что происходит в операторе 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 , я боюсь, но, вероятно, выполнимо).

38
ответ дан 7 November 2019 в 11:40
поделиться

Проблема в том, что python использует отдельные коды операций для печати NEWLINE и печати самого объекта. Самое простое решение - просто использовать явный sys.stdout.write с явным переводом строки.

23
ответ дан 7 November 2019 в 11:40
поделиться

Я не знаю, есть ли лучший способ вместо этого механизма блокировки, но, по крайней мере, это выглядит просто. Я также не уверен, что печать действительно не является потокобезопасной.

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')
13
ответ дан 7 November 2019 в 11:40
поделиться
Другие вопросы по тегам:

Похожие вопросы: