[英]Do I need a lock block to iterate on a shared list?
我知道python列表是線程安全的。 但是,假設我有一個共享列表
object_list = []
現在,線程定期向此列表添加一個對象(數據結構),
object_list.add(aNewObject)
調用時,另一個線程在for循環中訪問此列表
for i in object_list:
#do something with i
我需要這樣的東西:
myLock.acquire()
either add an element or access the list with the for
myLock.release()
或者我還好嗎? 我不關心錯過添加,但我擔心如果在讀取線程在列表中間迭代時添加新元素會發生什么...
這取決於您期望的保證。 首先,Python語言並不能保證任何線程安全。
由於它的GIL,CPython實現偶然提供了一些線程安全性,但是如果你計划在任何其他python實現下運行代碼,那么只要你訪問資源就應該使用鎖。
現在,對於CPython特定的考慮,如果你知道代碼只會通過append
或extend
添加元素,那么可能發生的最壞情況是for
循環也將遍歷新元素。 這可能是也可能不是你想要的東西1 :
>>> a = [0]
>>> for i, el in enumerate(a):
... if i % 2 == 0:
... a.append(i)
... print(i, el)
...
0 0
1 0
>>> a
[0, 0]
>>> for i, el in enumerate(a):
... if i % 2 == 0:
... a.append(i)
... print(i, el)
...
0 0
1 0
2 0
3 2
>>> a
[0, 0, 0, 2]
如果使用insert
方法添加新項,那么有些值可能會迭代一次以上,而其他一些值則不會在for
期間迭代。 例如:
>>> a = [1]
>>> for i,el in enumerate(a):
... if i % 2 == 0:
... a.insert(0, i)
... print(i, el)
...
(0, 1)
(1, 1)
>>> a
[0, 1]
這里1
迭代兩次,並且在loop
從未看到新插入的0
。
同樣從列表中刪除元素可能會導致跳過某些元素的類似行為:
>>> for i, el in enumerate(a):
... if i % 2 == 0:
... a.remove(i)
... print(i, el)
...
(0, 0)
>>> a
[1]
正如您所看到的,循環期間從未見過1
。
這說在迭代期間改變序列通常是你不想要的。 在你的情況下,我建議使用鎖。 請注意,鎖是上下文管理器,因此我將循環編寫為:
with list_lock:
for element in object_list:
# do stuff
這簡化了代碼,因為您不必記住明確釋放鎖。 插入元素時應該這樣做(否則它是無用的):
with list_lock:
object_list.append(new_element)
1我不使用單獨的線程來簡化示例並使其易於重現。 在CPython中,在多個線程中完成的操作不應該改變您看到的行為。 在其他實現中不是這樣。 顯然,使用多線程更難以重現行為。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.