Python中的set.copy()方法是原子操作吗?

3
假设我们在一个类中有一个实例变量。
class MyClass(object):  
    def __init__():  
        self.set1 = set()  

以下操作是否是原子性的?
set_shallow_copy = self.set1.copy()

我尝试搜索了一下,但我找到的唯一信息是读取实例变量是原子性的。
(编辑) 我尝试反编译字节码(Windows上的Python 2.7),但这并不是太有用。它只显示调用set上的copy函数的Python字节码。
 9 LOAD_FAST                0 (s)  
 12 LOAD_ATTR                1 (copy)  
 15 CALL_FUNCTION            0 
 18 STORE_FAST               1 (s2) 
 21 LOAD_CONST               0 (None) 
 24 RETURN_VALUE 

(编辑2)我想知道使用set2 = set(set1)是否更好。
         9 LOAD_GLOBAL              0 (set)  
         12 LOAD_FAST                0 (set1)  
         15 CALL_FUNCTION            1  
         18 STORE_FAST               1 (set2)  
         21 LOAD_CONST               0 (None)  
         24 RETURN_VALUE

我在想我们是在使用Python3还是Python2.7? - DataEngineer
1
现在我正在使用Python 2.7。在Python 3中会有所不同吗? - Li Chen Koh
1
你为什么在意?调用copy方法比调用内置的set构造函数稍微快一点,但这与原子性无关。目标是什么? - ShadowRanger
我怀疑这可能取决于您所谓的“原子”是什么意思。大多数内置对象的操作在“原子”意义上都是“原子”的,因为它们不会让另一个线程中的操作破坏它们的内部状态。这并不意味着操作一定会按特定顺序运行,只是该对象最终会处于一个合理的状态。 - Blckknght
我明白了,我认为在结束时使对象处于正常状态就足够了。我不需要保证操作按特定顺序运行。 - Li Chen Koh
2个回答

2
在CPython自3.5版本以来,由于全局解释器锁的缘故,从Python中可见,复制是原子性的。在复制时,没有其他线程可以更改set1,因此您将获得该集合在(其他)线程进行操作期间处于的某个状态的副本。
在旧版本中(如此处标记的版本!),将原始集合的元素添加到新的(最初为空的)副本的例程没有利用来自set的所有值都是唯一的这一事实;因此,它使用==来重新发现这个事实。如果该比较在Python中实现(或由某些C扩展程序释放GIL),则该过程可能会被其他线程中断(并任意失败)。

你确定吗,还是有权威的参考资料支持?我刚刚在一行代码里遇到了这个问题:my_set_copy = my_set.copy(),报错信息是 RuntimeError: Set changed size during iteration(而且确实是另一个线程修改了这个 set)。 - bluenote10
@bluenote10:使用哪个Python版本?当前的实现甚至不需要调用用户定义的__eq__,因为它知道源键是唯一的。 - Davis Herring
那是在运行Python 2.7.12的旧系统上。 - bluenote10
1
@bluenote10:好吧,那就是问题所在的版本,这很公平。我已经添加了该版本的信息,答案有时是正确的。之前表述过于简单,抱歉。 - Davis Herring
非常有帮助,感谢您挖掘出这些差异。 - bluenote10

-1
请在共享原子企业中使用automic_set。
Python原子用于共享数据类型。

https://sharedatomic.top

该模块可用于多进程和多线程条件下的原子操作。高性能Python!高并发,高性能!

使用多进程和多线程的原子API示例:

您需要以下步骤来利用该模块:

  1. 创建函数,供子进程使用,参考UIntAPIs、IntAPIs、BytearrayAPIs、StringAPIs、SetAPIs、ListAPIs,在每个进程中,您可以创建多个线程。

     def process_run(a):
       def subthread_run(a):
         a.array_sub_and_fetch(b'\0x0F')
    
       threadlist = []
       for t in range(5000):
           threadlist.append(Thread(target=subthread_run, args=(a,)))
    
       for t in range(5000):
           threadlist[t].start()
    
       for t in range(5000):
           threadlist[t].join()
    
  2. 创建共享的bytearray

    a = atomic_bytearray(b'ab', length=7, paddingdirection='r', paddingbytes=b'012', mode='m')
    
  3. 启动进程/线程以利用共享的bytearray

     processlist = []
    
     for p in range(2):
    
       processlist.append(Process(target=process_run, args=(a,)))
    
     for p in range(2):
    
       processlist[p].start()
    
     for p in range(2):
    
       processlist[p].join()
    
     assert a.value == int.to_bytes(27411031864108609, length=8, byteorder='big')
    

这个和Python是否有原子CompareAndSet操作?是你在Python原子数据类型上的回答的复制粘贴(除了关于automic_set的第一句话)。不要在多个地方重复相同的答案。 - Peter Cordes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接