2011-06-11 85 views
96

我注意到它經常被建議使用多線程隊列,而不是列表和.pop()。這是因爲列表不是線程安全的或者其他原因?列表線程安全嗎?

回答

124

列表本身是線程安全的。在CPython中,GIL可以防止對它們的併發訪問,其他實現會爲列表實現注意使用細粒度鎖定或同步數據類型。然而,雖然列表本身不能因嘗試同時訪問而損壞,但列表的數據未受保護。例如:

L[0] += 1 

不能保證由一個,如果另一線程做同樣的事情,以實際上增加L [0],因爲+=不是一個原子操作。 (Python中非常少的操作實際上是原子操作,因爲其中大多數操作都可能導致任意Python代碼被調用。)應該使用隊列,因爲如果您只是使用不受保護的列表,您可能會獲取或刪除錯誤的項目,因爲的競賽條件。

+0

Deque也是線程安全的嗎?這似乎更適合我的使用。 – lemiant 2011-06-12 00:06:20

+15

所有Python對象都具有相同的線程安全性 - 它們本身不會損壞,但它們的數據可能會損壞。 collections.deque是Qu​​eue.Queue對象的後面。如果你從兩個線程訪問東西,你應該使用Queue.Queue對象。真。 – 2011-06-12 00:09:31

+0

lemiant,deque是線程安全的。從Fluent Python的第2章:「類collections.deque是一個線程安全的雙端隊列,設計用於從兩端快速插入和刪除。[...] append和popleft操作是原子操作,因此deque是安全的在多線程應用程序中用作LIFO隊列 ,而不需要使用鎖。「 – 2018-01-10 00:12:37

53

爲了澄清托馬斯的優秀答案中的一點,應該提及append()線程安全。

這是因爲沒有擔心數據被讀取在同一個地方,一旦我們去它。 append()操作不讀取數據,它只將數據寫入列表。

+1

PyList_Append正在從內存中讀取。你的意思是它的讀寫發生在同一個GIL鎖? https://github.com/python/cpython/blob/1cecdbbf1adef9af2ac829c7dbba91352accb1fd/Objects/listobject.c#L303 – amwinter 2014-09-24 13:13:16

+1

@amwinter是的,對'PyList_Append'的整個調用是在一個GIL鎖中完成的。它被賦予一個要追加的對象的引用。在對對象的內容進行評估之後以及對「PyList_Append」的調用完成之前,該對象的內容可能會被更改。但它仍然是同一個對象,並且安全地附加了(如果你執行lst.append(x); ok = lst [-1]是x',那麼'ok'當然可能是False)。 您引用的代碼不會從附加對象中讀取,INCREF除外。它讀取並可能重新分配附加到的列表。 – greggo 2016-05-09 14:24:35

+1

dotancohen的觀點是'L [0] + = x'會在'L'上執行一個'__getitem__',然後在'L'上執行一個'__setitem__' - 如果'L'支持'__iadd__'它會做事在對象接口上有點不同,但是在python解釋器級別上對'L'仍然有兩個獨立的操作(你會在編譯的字節碼中看到它們)。 'append'是在字節碼中的單個方法調用中完成的。 – greggo 2016-05-09 14:33:05