2017-01-02 130 views
2

我想测试它是否可以将追加到来自两个线程列出,但我发现了凌乱的输出:Python的多线程列表追加

import threading 


class myThread(threading.Thread): 
    def __init__(self, name, alist): 
     threading.Thread.__init__(self) 
     self.alist = alist 

    def run(self): 
     print "Starting " + self.name 
     append_to_list(self.alist, 2) 
     print "Exiting " + self.name 
     print self.alist 


def append_to_list(alist, counter): 
    while counter: 
     alist.append(alist[-1]+1) 
     counter -= 1 

alist = [1, 2] 
# Create new threads 
thread1 = myThread("Thread-1", alist) 
thread2 = myThread("Thread-2", alist) 

# Start new Threads 
thread1.start() 
thread2.start() 

print "Exiting Main Thread" 
print alist 

所以输出:

Starting Thread-1 
Exiting Thread-1 
Starting Thread-2 
Exiting Main Thread 
Exiting Thread-2 
[1[1, 2[, 1, 2, 23, , 34, 5, 6, ]4 
, 5, , 3, 64, 5, ]6] 

为什么它太乱了,alist不等于[1,2,3,4,5,6]?

+0

如果从run()方法中删除print self.alist,它会起作用吗? – snakecharmerb

+0

不,它仍然凌乱不等于[1,2,3,4,5,6] – Alexey

回答

2

编辑:@kroltan让我想更多一些,我认为你的例子实际上是更多的线程安全,然后我原本以为。这个问题是不是在总多写线程,它在这条线的具体做法是:

alist.append(ALIST [-1] +1)

有没有保证,append会发生紧接在alist[-1]完成之后,其他操作可以被交错。

对于此处的详细说明: http://effbot.org/pyfaq/what-kinds-of-global-value-mutation-are-thread-safe.htm

操作该更换时,他们的引用计数为零的其他对象可以调用与其他对象的德尔方法,并能影响的东西。对于词典和列表的大量更新尤其如此。如有疑问,请使用互斥体!

原来的答案:

这是不确定的行为,如你有多个线程写入 相同的内存位 - 因此“混乱”输出你的观察。

我想测试它是否可以将追加到来自两个线程列出,但我发现了凌乱的输出

我想你已经成功地进行了测试,答案是否定的 很多上SO更详细的解释: https://stackoverflow.com/a/5943027/62032

+1

它真的没有定义?列表当然是线程安全的。同时写入标准输出当然是*不可预测的,但是定义明确。 – Kroltan

+0

列表是线程安全的。你的第一个陈述是正确的 - 由于其他操作进入检索)alist [-1]和对append的调用之间,列表的数字不会单调增长。但是,“杂乱”的输出是由于印刷声明:一个是线程安全的,并且两个调用的输出都是双重的。尽管如此,结果仍然只包含int对象。 – jsbueno

2

摘要

为什么输出很乱?

==>因为线程可通过执行print语句

得到部分地为什么aList不等于[1,2,3,4,5,6]?

==>因为aList的内容可能会在从它读取和追加 它之间改变。

输出

输出是凌乱的,因为它是由python2的print声明 从线程内产生,并且print说法是不threadsafe。这意味着 线程可能产生,而print正在执行。在 问题的代码中存在多个线程打印,因此一个线程可能会产生打印时,另一个线程可能会开始打印,然后产生,从而产生OP所看到的交错输出。 IO操作,如写stdout 在CPU方面很慢,所以它很可能是操作系统可能 暂停执行IO,因为线程正在等待对硬件做 什么的线程。

例如,这样的代码:

import threading 


def printer(): 
    for i in range(2): 
     print ['foo', 'bar', 'baz'] 


def main(): 
    threads = [threading.Thread(target=printer) for x in xrange(2)] 
    for t in threads: 
     t.start() 
    for t in threads: 
     t.join() 

产生此交织输出:

def printer(): 
    for i in range(2): 
     with lock: 
      print ['foo', 'bar', 'baz'] 


def main(): 
    global lock 
    lock = threading.Lock() 
    threads = [threading.Thread(target=printer) for x in xrange(2)] 
    for t in threads: 
     t.start() 
    for t in threads: 
     t.join() 

>>> main() 
['foo', 'bar', 'baz'] 
['foo', 'bar', 'baz'] 
['foo', 'bar', 'baz'] 
['foo', 'bar', 'baz'] 

内容:

>>> main() 
['foo', 'bar'['foo', , 'bar', 'baz'] 
'baz'] 
['foo', ['foo', 'bar''bar', 'baz'] 
, 'baz'] 

交织行为可以通过使用lock可以防止的名单

aList最终内容将是[1, 2, 3, 4, 5, 6]如果语句

aList.append(aList[-1] + 1)

被自动执行,即不当前线程屈服于另一个线程 这也是读取和追加到aList

但是,这不是线程的工作原理。一个线程可能读取 的最后一个元素从aList或递增值后得到,所以这是很 可能具有事件的这样一个序列:

  1. 线程1从aList
  2. 线程1的产率读取值2
  3. 线程2读取来自aList2,然后追加3
  4. 线程2从读取值,然后追加4
  5. 线程2产生
  6. 线程1追加3
  7. 线程1读取来自aList3,然后追加4

这留下aList作为[1, 2, 3, 4, 3, 4]

如同print语句,这可以通过让线程获得来防止执行aList.append(aList[-1] + 1)

前(注意list.append方法是在纯Python代码threadsafe,因此不存在危险,即值被附加可能会损坏。)

0

由于正在使用相同的变量,以读,写,那就有一个未定义的行为,我执行的代码,并在同一台机器上有两种不同的情况下,2个不同的输出:

Starting Thread-1 
Exiting Thread-1 
[1, 2, 3, 4]Starting Thread-2 

Exiting Main Thread 
[Exiting Thread-21, 2, 3, 4 
, [51, , 62], 
3, 4, 5, 6] 

Starting Thread-1 
Exiting Thread-1 
[1, 2, 3, 4] 
Exiting Main Thread 
[1, 2, 3, 4] 
Starting Thread-2 
Exiting Thread-2 
[1, 2, 3, 4, 5, 6] 

您应该使用同步来获得输出所需否则等待不确定的状态,让你正确的输出

编辑:你可以通过这篇文章了解如何实现同步http://theorangeduck.com/page/synchronized-python

+0

好的,掌握你的答案,但你能提供一些实现同步的代码吗? – Alexey

0

您需要使用threading.lock方法来确保在一个线程执行动作(例如打印输出到屏幕)时,它们不会干扰其他线程的动作。

+1

这不提供问题的答案。一旦你有足够的[声誉](https:// stackoverflow。com/help/whats-reputation)你将能够[对任何帖子发表评论](https://stackoverflow.com/help/privileges/comment);相反,[提供不需要提问者澄清的答案](https://meta.stackexchange.com/questions/214173/why-do-i-need-50-reputation-to-comment-what-c​​an- I-DO-代替)。 - [来自评论](/ review/low-quality-posts/17581699) – YPCrumble