簡體   English   中英

我是否需要鎖定塊來迭代共享列表?

[英]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特定的考慮,如果你知道代碼只會通過appendextend添加元素,那么可能發生的最壞情況是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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM