Python: 'not in' 期间列表更改大小会导致 UB 吗?
Python: Will A List Changing Size During 'not in' Cause UB?
我有一个有两个线程的假设程序。
items = [ 'A', 'B' ]
func1():
while True:
if 'C' not in items:
# do something
func2():
while True:
items.clear()
items.append('A')
items.append('B')
main():
start_new_thread(func1)
start_new_thread(func2)
当执行 not in
时,我假设 Python 必须在内部迭代列表以检查每个元素。如果func2
在迭代过程中改变了这个列表的大小,会不会导致异常或者UB?
永远不要允许两个线程在任何编程语言中同时访问同一块内存。这将创建一个 data race condition which leads to extremely undefined behavior (If your programming language of choice cannot detect this and throw an exception). The safest thing to do is use a lock 对象,如果另一个线程已获取锁,则强制停止执行一个线程,并等待直到可以安全地再次修改数据。这是添加了锁的最小示例(并且还修改为实际工作代码)。
import threading
items = [ 'A', 'B' ]
lock = threading.Lock()
def func1():
while True:
lock.acquire()
if 'C' not in items:
print("C not in items")
lock.release()
def func2():
while True:
lock.acquire()
items.clear()
items.append('A')
items.append('B')
lock.release()
def main():
thread_a = threading.Thread(target=func1, daemon=True)
thread_b = threading.Thread(target=func2, daemon=True)
thread_a.start()
thread_b.start()
thread_a.join()
thread_b.join()
main()
请注意,许多库可能将它们的某些函数称为“线程安全”。这意味着他们已经处理了任何潜在的数据竞争条件,您不必担心设置锁。您可能还会遇到“原子”操作,这基本上只是意味着它们是线程安全的操作。不过,原子操作在 python 中并不是真正的东西。
我有一个有两个线程的假设程序。
items = [ 'A', 'B' ]
func1():
while True:
if 'C' not in items:
# do something
func2():
while True:
items.clear()
items.append('A')
items.append('B')
main():
start_new_thread(func1)
start_new_thread(func2)
当执行 not in
时,我假设 Python 必须在内部迭代列表以检查每个元素。如果func2
在迭代过程中改变了这个列表的大小,会不会导致异常或者UB?
永远不要允许两个线程在任何编程语言中同时访问同一块内存。这将创建一个 data race condition which leads to extremely undefined behavior (If your programming language of choice cannot detect this and throw an exception). The safest thing to do is use a lock 对象,如果另一个线程已获取锁,则强制停止执行一个线程,并等待直到可以安全地再次修改数据。这是添加了锁的最小示例(并且还修改为实际工作代码)。
import threading
items = [ 'A', 'B' ]
lock = threading.Lock()
def func1():
while True:
lock.acquire()
if 'C' not in items:
print("C not in items")
lock.release()
def func2():
while True:
lock.acquire()
items.clear()
items.append('A')
items.append('B')
lock.release()
def main():
thread_a = threading.Thread(target=func1, daemon=True)
thread_b = threading.Thread(target=func2, daemon=True)
thread_a.start()
thread_b.start()
thread_a.join()
thread_b.join()
main()
请注意,许多库可能将它们的某些函数称为“线程安全”。这意味着他们已经处理了任何潜在的数据竞争条件,您不必担心设置锁。您可能还会遇到“原子”操作,这基本上只是意味着它们是线程安全的操作。不过,原子操作在 python 中并不是真正的东西。