Python线程与奇怪的输出错误

时间:2016-10-12 22:07:45

标签: python multithreading

我是新手一般的线程,并且一直在玩弄不同的想法让我脚踏实地。但是,我遇到了一些我不确定如何解释的事情。这是代码:

import threading, datetime
class ThreadClass(threading.Thread):
    def run(self):
        for _ in range(3):
            now = datetime.datetime.now()
            print "%s: %s" %(self.getName(), now)

for i in range(2):
    t = ThreadClass()
    t.start()

程序正是我想要它做的,但是,输出中有一些随机错误。

Thread-1: 2016-10-12 17:34:23.012462
Thread-1: 2016-10-12 17:34:23.012802
 Thread-2: 2016-10-12 17:34:23.013025
Thread-2: 2016-10-12 17:34:23.013108

Thread-2: 2016-10-12 17:34:23.013225
Thread-1: 2016-10-12 17:34:23.013252

错误相对一致(即输出中显示相同的空格和新行,只是在不同的地方)。我怀疑它与几乎同时写入输出流的线程有关,但是在这个特定的运行中,当Thread-2运行时会发生两个错误。

非常感谢任何帮助描述为什么会出现这个问题!

我在Ubuntu 14.04(双核CPU)上使用Python 2.7运行此代码,如果这会有所不同。

3 个答案:

答案 0 :(得分:2)

对Joseph的回复作补充,你可以使用Semaphore

from threading import Semaphore
writeLock = Semaphore(value = 1)

...

当您打算在帖子中打印时:

writeLock.acquire()
print ("%s: %s" %(self.getName(), now))
writeLock.release()

writeLock确保在release()之前的任何一个时刻只能打印一个线程。

想象一下信号量作为苍蝇之王的海螺"只有得到它的人能说话。当第一个发言者结束时,他将它交给下一位发言者。

答案 1 :(得分:1)

您正在寻找logging模块:

  

15.7.9。线程安全

     

日志记录模块旨在保证线程安全,而无需客户进行任何特殊工作。它通过使用线程锁来实现这一点;有一个锁可以序列化对模块共享数据的访问,每个处理程序还会创建一个锁,以序列化对其底层I / O的访问。

以下是一个例子:

FORMAT = '%(threadName)s %(asctime)s'
logging.basicConfig(format=FORMAT, level=logging.DEBUG) # "level" default is to only print WARN and above.
logger = logging.getLogger('MyThreadClass')
logger.debug('')

答案 2 :(得分:1)

您的部分问题是为什么会发生这种情况?这是一个很好的问题,因为你会在输出中注意到有一个完全虚假的空间这两个线程都没有真正尝试打印

Thread-1: 2016-10-12 17:34:23.012802
 Thread-2: 2016-10-12 17:34:23.013025
^

那是怎么到达那里的?没有你的线程试图打印它!由于Python 2.x中print语句实现软间距的方式。

例如......当您执行print 1,时收到stdout

1              # there is no space after that 1 yet!

然后后续print 2将导致插入空格以提供最终输出:

1 2

发生的事情是print <something>,语句被编译为字节码操作PRINT_ITEMPRINT_ITEM的实施:

  • 检查输出流以查看此流的最后PRINT_ITEM是否标记为需要打印软空格;
  • 如果是,请打印“”;
  • 然后打印我们的项目并将流注释为需要另一个软空间。

还有另一个字节码操作PRINT_NEWLINE,它打印一个新行并清除流上的任何软空间标记。

现在,在您的代码中,每个线程都将执行以下操作:

print "%s: %s" %(self.getName(), now)

这会编译为:

PRINT_ITEM          # print the top of the stack
PRINT_NEWLINE       # print a new line

因此,线程交互可能意味着:

  • Thread-1可以执行PRINT_ITEM并将流标记为需要为下一个PRINT_ITEM提供软空间;
  • Thread-2可以开始PRINT_ITEM,并看到该流需要一个软空间;
  • Thread-1现在打印一个新行;
  • Thread-2现在打印该空间及其项目;

请注意,如果出现以下情况,则不会发生此错误:

  • 您使用sys.stdout.write来执行输出;或
  • 您使用的是具有不同io结构的Python 3.x