2017-01-02 129 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