如何在Python 2.6中获得线程安全打印? [英] How do I get a thread safe print in Python 2.6?

查看:107
本文介绍了如何在Python 2.6中获得线程安全打印?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

根据这些 解决方案

有趣的问题-考虑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...

串行化"效应(如上所示,线程看上去像轮流")是以下事实的副作用,即成为当前正在打印的线程比其他线程慢得多(所有线程那些等待!-).注释掉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 in Python is not thread safe according to these articles.

A Python 3 work-around is offered in the latter article.

How do I get a thread safe print in Python 2.6?

解决方案

Interesting problem -- considering all the things that happen within a print statement, including the setting and checking of the softspace attribute, making it "threadsafe" (meaning, actually: a thread that's printing only yields "control of standard output" to another thread when it's printing a newline, so that each entire line that's output is guaranteed to come from a single thread) was a bit of a challenge (the usual easy approach to actual thread safety -- delegating a separate thread to exclusively "own" and handle sys.stdout, communicate to it via Queue.Queue -- isn't all that useful, since the problem is not thread safety [[even with a plain print there is no risk of crashing and the characters that end up on standard output are exactly those which get printed]] but the need for mutual exclusion among threads for an extended range of operations).

So, I think I made it...:

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'

The calls to wait are intended to guarantee chaotically mixed output in the absence of this mutual exclusion guarantee (whence the comment). With the wrapping, i.e., the above code exactly as it looks there, and (at least) Python 2.5 and up (I believe this may run in earlier versions, too, but I don't have any easily at hand to check) the output is:

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...

The "serialization" efect (whereby the threads appear to "nicely round-robin" as above) is a side effect of the fact that the thread that gets to be the currently-printing one is seriously slower than the others (all those waits!-). Commenting out the time.sleep in wait, the output is instead

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...

i.e. a more typical "multithreaded output"... except for the guarantee that each line in the output comes entirely from one single thread.

Of course, a thread that does, e.g., print 'ciao', will keep "ownership" of standard output until it finally does perform a print without a trailing comma, and other threads wanting to print may sleep for quite a while (how else can one guarantee that each line in the output comes from a single thread? well, one architecture would be to accumulate partial lines to thread local storage instead of actually writing them to standard output, and only do the writing on receipt of the \n... delicate to interleave properly with softspace settings, I fear, but probably feasible).

这篇关于如何在Python 2.6中获得线程安全打印?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆