Python中的线程:类属性(列表)不是线程安全的?
我试图理解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
# -*- 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()
等于2时,才会将值“Test value”添加到属性(列表)\uu id
\uu属性中,该属性不应共享
检查列表dummy2()
的大小是否严格优于0,然后\u属性atshouldnotbeshared
- 对于不应共享的
\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:我认为你实际上没有看到同步问题。但是,除了您看到的问题之外,很可能每隔一段时间(很少,但这只会使调试更加痛苦)一个线程就会看到共享列表被另一个线程更新了一半。