我知道python列表是线程安全的.但是,假设我有一个共享列表
object_list = []
Run Code Online (Sandbox Code Playgroud)
现在,线程定期向此列表添加一个对象(数据结构),
object_list.add(aNewObject)
Run Code Online (Sandbox Code Playgroud)
调用时,另一个线程在for循环中访问此列表
for i in object_list:
#do something with i
Run Code Online (Sandbox Code Playgroud)
我需要这样的东西:
myLock.acquire()
either add an element or access the list with the for
myLock.release()
Run Code Online (Sandbox Code Playgroud)
或者我还好吗?我不关心错过添加,但我担心如果在读取线程在列表中间迭代时添加新元素会发生什么...
这取决于您期望的保证。首先,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]
Run Code Online (Sandbox Code Playgroud)
如果使用该方法添加新项目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]
Run Code Online (Sandbox Code Playgroud)
这里1迭代了两次并且新插入了0在 中从未出现过loop。
从列表中删除元素也可能会导致类似的行为,其中某些元素会被跳过:
>>> for i, el in enumerate(a):
... if i % 2 == 0:
... a.remove(i)
... print(i, el)
...
(0, 0)
>>> a
[1]
Run Code Online (Sandbox Code Playgroud)
正如你所看到的1在循环期间从未见过。
这就是说,在迭代期间改变序列通常是您不希望发生的事情。对于你的情况,我建议使用锁。请注意,锁是上下文管理器,因此我将循环编写为:
with list_lock:
for element in object_list:
# do stuff
Run Code Online (Sandbox Code Playgroud)
这简化了代码,因为您不必记住显式释放锁。插入元素时也应该这样做(否则没用):
with list_lock:
object_list.append(new_element)
Run Code Online (Sandbox Code Playgroud)
1我不使用单独的线程来简化示例并使其易于重现。在 CPython 中,在多个线程中完成操作不应改变您看到的行为。在其他实现中情况并非如此。显然,使用多个线程来重现该行为要困难得多。
| 归档时间: |
|
| 查看次数: |
1177 次 |
| 最近记录: |