2012-03-01 47 views
5

說我有一個列表l和線程T1循環訪問l永遠:確實python凍結列表for循環之前?

while True: 
    for i in l: 
     #do something 

而另一個線程T2隨機在l修改或刪除成員。

刪除後會發生什麼? t1是否在當前循環中檢測到?

UPDATE

  1. 通過凍結我的意思是T1得到l副本。 T2可以修改l爲 肯定

  2. 引用文檔或簡單,但說服的代碼片段被 歡迎。

+0

你*嘗試了嗎? – 2012-03-01 11:41:17

+2

@ChrisMorgan種族條件錯誤是非常難以找到的。因此,嘗試它只能表明存在明顯的錯誤,並且不能以其他方式顯示。更糟的是,cpython的GIL會一直隱藏錯誤。 – phihag 2012-03-01 11:42:59

+2

@ChrisMorgan我在當前的項目中提出了這個問題,但是這並沒有觸發致命錯誤,我找不到一個簡單而有說服力的例子來揭示真相。 – onemach 2012-03-01 11:46:53

回答

7

編號列表不結冰 - 你的迭代器將拋出異常的感覺不「破發」。相反,它將繼續在列表中前進,結果可能令人驚訝。

考慮這個代碼(摘錄於ideone這裏:http://ideone.com/0iMao):

l = list(range(10)) 
for i in l: 
    print i 
    try: 
     del l[i] 
    except (RuntimeError,IndexError), e: 
     print e 

print l 

它產生這樣的輸出:

0 
2 
4 
5 
7 
list assignment index out of range 
9 
list assignment index out of range 
[1, 2, 4, 5, 7, 9] 

這可能不是你想要的或預期的,但它顯然很好 - 定義:http://docs.python.org/reference/compound_stmts.html#the-for-statement

相反,您可以鎖定列表或進行復制。請注意,iter(l)不會在內部複製副本,並且與直接在列表中迭代相同。

+1

+1給寒意舉例 – Simon 2012-03-01 12:40:00

0

該列表可從兩個線程訪問並且不凍結。 迭代器將「知道」成員已被刪除並跳過它們,除非您擁有超出列表長度的顯式索引訪問權限。 刪除本身是線程安全的。

如果您想要列表的真正鎖定,用互斥鎖保護它或將它複製。

2

這裏是諸如此類的事情,你可以觀察到:

>>> from threading import Thread 
>>> from time import sleep 
>>> liszt = ['first item', 'second item', 'third item', 'fourth item', 
...   'plentee more items', "but I'm lazy"] 
>>> def thread_one(): 
...  for i in liszt: 
...    print 'Thread one found "%s"' % i 
...    sleep(1) 
... 
>>> def thread_two(): 
...  sleep(0.5) 
...  print 'Thread two deleting first item.' 
...  del liszt[0] 
...  sleep(1) 
...  print 'Thread two deleting fourth item.' 
...  del liszt[3] 
... 
>>> Thread(target=thread_one).start(); Thread(target=thread_two).start() 
Thread one found "first item" 
Thread two deleting first item. 
Thread one found "third item" 
Thread two deleting fourth item. 
Thread one found "fourth item" 
Thread one found "but I'm lazy" 

從這裏就可以看出,修改一個線程列表中影響了其他線程的迭代器;刪除第一個項目使迭代器跳過一個項目,刪除未來的項目意味着它將不會在迭代器中看到,因爲它已經消失。

這是一些如何工作的模型;我沒有明確提供代碼,但你可以從觀察中解決這個問題。

State:    [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 
Iterator position:^

轉到下一項。

State:    [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 
Iterator position: ^

刪除第一項。

State:    [2, 3, 4, 5, 6, 7, 8, 9, 10] 
Iterator position: ^

轉到下一項。

State:    [2, 3, 4, 5, 6, 7, 8, 9, 10] 
Iterator position:  ^

刪除第五項。

State:    [2, 3, 4, 5, 7, 8, 9, 10] 
Iterator position:  ^

et cetera。關於線程問題,無論您是在一個線程還是多個線程中執行,都無關緊要。當然,您可能會遇到該項目是否被刪除的競爭條件,但它仍然以相同的方式工作。

如果你不熟悉迭代的內部,遇見iter。實際上,for x in y正在迭代iter(y)。所以,如果你願意的話,你可以玩iter(liszt) listiterator對象,當你在迭代它的列表時,使用next()。在交互式Python控制檯中比for循環更方便。