Python中的线程:类属性(列表)不是线程安全的?

Python中的线程:类属性(列表)不是线程安全的?,python,multithreading,list,class,object,Python,Multithreading,List,Class,Object,我试图理解Python中的线程 代码 现在我有一个问题,我把它放在一个简单的类中: # -*- coding: utf-8 -*- import threading class myClassWithThread(threading.Thread): __propertyThatShouldNotBeShared = [] __id = None def __init__(self, id): threading.Thread.__init__(sel

我试图理解Python中的线程

代码

现在我有一个问题,我把它放在一个简单的类中:

# -*- coding: utf-8 -*-
import threading

class myClassWithThread(threading.Thread):

    __propertyThatShouldNotBeShared = []
    __id = None
    def __init__(self, id):
        threading.Thread.__init__(self)
        self.__id = id

    def run(self):
        while 1:
            self.dummy1()
            self.dummy2()

    def dummy1(self):
        if self.__id == 2:
            self.__propertyThatShouldNotBeShared.append("Test value")


    def dummy2(self):
        for data in self.__propertyThatShouldNotBeShared:
            print self.__id
            print data
            self.__propertyThatShouldNotBeShared.remove(data)



obj1 = myClassWithThread(1)
obj2 = myClassWithThread(2)
obj3 = myClassWithThread(3)

obj1.start()
obj2.start()
obj3.start()
说明

以下是该课程的内容: 该类有两个属性:

  • \uuuu id
    ,它是对象的标识符,在调用构造函数时给出

  • \u属性不应共享的属性是一个列表,将包含一个文本值
现在是方法

  • run()
    包含一个无限循环,我在其中调用
    dummy1()
    然后调用
    dummy2()

  • dummy1()
    仅当对象的
    \uu id
    等于2时,才会将值“Test value”添加到属性(列表)
    \uu属性中,该属性不应共享

  • dummy2()
    检查列表
    \u属性atshouldnotbeshared
    的大小是否严格优于0,然后

    • 对于不应共享的
      \u属性中的每个值,它将打印

    • \u属性中包含的对象和值不应共享
    • 然后删除该值
以下是我启动程序时得到的输出:

21

Test valueTest value

2
Test value
Exception in thread Thread-2:
Traceback (most recent call last):
  File "E:\PROG\myFace\python\lib\threading.py", line 808, in __bootstrap_inner
    self.run()
  File "E:\PROG\myFace\myProject\ghos2\src\Tests\threadDeMerde.py", line 15, in run
    self.dummy2()
  File "E:\PROG\myFace\myProject\ghos2\src\Tests\threadDeMerde.py", line 27, in dummy2
    self.__propertyThatShouldNotBeShared.remove(data)
ValueError: list.remove(x): x not in list
问题

正如您在输出的第一行中所看到的,我得到了这个“1”…这意味着,在某个点上,id为“1”的对象试图在屏幕上打印一些东西…实际上它做到了! 但这应该是不可能的! 只有id为“2”的对象才能打印任何内容

这段代码有什么问题?或者我的逻辑有什么问题?

问题是:

class myClassWithThread(threading.Thread):
    __propertyThatShouldNotBeShared = []
它为共享的所有对象定义一个列表。您应该这样做:

class myClassWithThread(threading.Thread):
    def __init__(self, id):
        self.__propertyThatShouldNotBeShared = []
        # the other code goes here

Python中的类变量只是:由类的所有实例共享。您需要一个实例变量,通常在
\uuuu init\uuuu
中定义。删除类级声明(以及双前导下划线,它们用于名称损坏,您不需要在此处使用。)

这里有两个问题,一个是您询问的问题,线程安全,另一个是您没有询问的问题,即类和实例属性之间的差异



是后者造成了你的实际问题。类属性由该类的所有实例共享。它与这些实例是在单个线程上访问还是在多个线程上访问无关;只有一个不应该共享的
\u属性是每个人共享的。如果需要实例属性,则必须在实例上而不是在类上定义它。像这样:

class myClassWithThread(threading.Thread):
    def __init__(self, id):
        self.__propertyThatShouldNotBeShared = []

一旦您这样做了,每个实例都有自己的不应该共享的_属性副本,并且每个实例都生活在自己的线程上,因此不可能存在线程安全问题

但是,您的原始代码确实存在线程安全问题

几乎没有任何东西是自动线程安全的(也称为“同步”);异常(如)将明确地说明这一点,并且专门用于线程编程

您可以通过三种方式避免这种情况:

  • 不要分享任何东西
  • 不要改变你分享的任何东西
  • 除非有适当的同步对象保护,否则不要修改共享的任何内容
最后一个当然是最灵活的,但也是最复杂的。事实上,这正是人们为什么要考虑线程编程困难的原因。 简短的版本是,无论您在何处修改或访问共享的可变数据,例如
self.\u属性不应共享
,您都需要持有某种类型的同步对象,例如。例如:

class myClassWithThread(threading.Thread):
    __lock = threading.Lock()
    # etc.

    def dummy1(self):
        if self.__id == 2:
            with self.__lock:
                self.__propertyThatShouldNotBeShared.append("Test value")

如果您坚持使用CPython和内置类型,通常可以忽略锁。但线程编程中的“经常”只是“总是在测试和调试期间,直到发布或大型演示文稿时,突然开始失败”的同义词。除非您想了解全局解释器锁和内置类型在CPython中如何工作的规则,否则不要依赖于此。

\uuuu propertythyatshouldnotbeshared
是类成员而不是实例成员。嗯,很有趣!如何使其成为实例成员?是的,我的问题是Python本身没有正确学习(完全是我的错)。现在我意识到我已经用类属性对项目中的所有内容进行了编程!我只是很惊讶,我的项目一直工作到今天与崩溃:)谢谢你的答复!谢谢你的回答:)到目前为止,我没有分享任何东西…这就是为什么我对“共享”感到如此惊讶-数据行为,或者换句话说,同步问题:)@user3147478:我认为你实际上没有看到同步问题。但是,除了您看到的问题之外,很可能每隔一段时间(很少,但这只会使调试更加痛苦)一个线程就会看到共享列表被另一个线程更新了一半。