我不理解found here下方示例代码的输出。当前线程和工作线程具有相同的地址,这怎么可能?
from PySide import QtCore
class Master(QtCore.QObject):
command = QtCore.Signal(str)
def __init__(self):
QtCore.QObject.__init__(self)
class Worker(QtCore.QObject):
def __init__(self):
QtCore.QObject.__init__(self)
def do_something(self, text):
print('in thread {} message {}'.format(QtCore.QThread.currentThread(), text))
if __name__ == '__main__':
app = QtCore.QCoreApplication([])
print(QtCore.QThread.currentThread())
# give us a thread and start it
thread = QtCore.QThread()
thread.start()
print(thread)
# create a worker and move it to our extra thread
worker = Worker()
worker.moveToThread(thread)
# create a master object and connect it to the worker
master = Master()
master.command.connect(worker.do_something)
# call a method of the worker directly (will be executed in the actual thread)
worker.do_something('in main thread')
# communicate via signals, will execute the method now in the extra thread
master.command.emit('in worker thread')
# start the application and kill it after 1 second
QtCore.QTimer.singleShot(1000, app.quit)
app.exec_()
# don't forget to terminate the extra thread
thread.quit()
输出:
<PySide.QtCore.QThread object at 0x0000000002537688>
<PySide.QtCore.QThread object at 0x0000000002537688>
in thread <PySide.QtCore.QThread object at 0x00000000025377C8> message in main thread
in thread <PySide.QtCore.QThread object at 0x0000000002537688> message in worker thread
答案 0 :(得分:8)
没有“包装器被重用”,只是旧的包装器对象(已删除)碰巧与新的包装器对象驻留在同一个内存地址。
PySide使用shiboken
库将QObject
包装到Python对象中。 shiboken documentation说明如下:
任何python绑定,基于项目的绑定都使用引用计数来处理包装器对象的生命周期(包含C ++对象的Python对象,不要与包装的C ++对象混淆)。当引用计数达到零时,Python垃圾收集器删除包装器并尝试删除包装的实例,但有时包装的C ++对象已被删除,或者C ++对象可能不会被释放Python包装器超出范围并死掉,因为C ++已经在处理被包装的实例。
除此之外,CPython malloc实现(以及许多其他常见的mallocs)often reuse the memory address that belonged to just deleted object随后创建了对象,这导致了常见的陷阱:
>>> {} is {}
False
>>> id({}) == id({})
True
因此,总是使得活对象的地址/ id是不同的;曾经生活过,然后死去的物品的地址只是好奇心。
在第一种情况下,两个字典的引用都被保留了,所以从技术上讲,它们id
是截然不同的,而在第二种情况下左手边的字典在左手后不久就被删除了 id被调用,之后才创建右手词典,并在相同的内存地址分配。
然而,它有点复杂了。当你调用currentThread()
时,如果旧的包装器仍处于活动状态(意味着它在python端引用了它),它将为QThread
返回一个新的包装器对象,除了 ,在这种情况下,返回旧的包装器:因此,
QtCore.QThread.currentThread() is QtCore.QThread.currentThread()
永远是真的!
现在,您在
中获得2个地址的原因in thread <PySide.QtCore.QThread object at 0x00000000028EB888> message in main thread
in thread <PySide.QtCore.QThread object at 0x00000000028EB8C8> message in worker thread
再一次不一定与包装纸“不被重复使用”无关,或者它们被保持;相反,在它们之间创建的其他一些对象可能会发生同样的效果,它们的引用被保留 - 也就是说,不应该认为包装对象保持活着。
但是,如果你持有对2个不同QThread
个对象的引用,那么它们的id()
将在其生命周期中不同。
总而言之,你不应该依赖shiboken包装器对象的地址做任何有用的事情;相反,如果你自己在Python代码中持有引用,那么is
测试是有用的
请注意__str__
的{{1}}将打印QObject
(如果已设置);因此,您可以通过以下方式轻松了解输出:
objectName
并且更清晰app = QtCore.QCoreApplication([])
QtCore.QThread.currentThread().setObjectName('main thread')
thread = QtCore.QThread()
thread.start()
thread.setObjectName('worker thread')
:
print
消息将是:
print('in thread: "{}" - message: "{}"'
.format(QtCore.QThread.currentThread().objectName(), text))
使用fetch the address of the underlying C++ object模块还有shiboken的方法;这对于调试其他东西也很方便;唉,默认情况下,Ubuntu 14.10上根本没有安装Python库,也没有安装任何与shiboken相关的软件包;经过无数次的回答,我终于设法安装了它。
结果比我想象的更可怕:
in thread: "main thread" - message: "in main thread"
in thread: "worker thread" - message: "in worker thread"
打印:
app = QtCore.QCoreApplication([])
print(QtCore.QCoreApplication.instance().thread())
obj = QtCore.QObject()
print(obj.thread())
del obj
print(QtCore.QThread.currentThread())
没错,在删除QObject后,线程包装器的地址发生了变化!因此,让我们从<PySide.QtCore.QThread object at 0x7fb4a1149cc8>
<PySide.QtCore.QThread object at 0x7fb4a1149cc8>
<PySide.QtCore.QThread object at 0x7fb4a1149d08>
导入dump
:
Shiboken.shiboken
输出
from Shiboken.shiboken import dump
def info(obj):
print(id(obj))
print(dump(obj))
app = QtCore.QCoreApplication([])
info(QtCore.QCoreApplication.instance().thread())
obj = QtCore.QObject()
info(obj.thread())
del obj
info(QtCore.QCoreApplication.instance().thread())
也就是说,shiboken生成 last 对象,其140323585370568
C++ address....... PySide.QtCore.QThread/0xe3d880
hasOwnership...... 0
containsCppWrapper 0
validCppObject.... 1
wasCreatedByPython 0
parent............ <PySide.QtCore.QCoreApplication object at 0x7f9fa175a948>
140323585370568
C++ address....... PySide.QtCore.QThread/0xe3d880
hasOwnership...... 0
containsCppWrapper 0
validCppObject.... 1
wasCreatedByPython 0
parent............ <PySide.QtCore.QObject object at 0x7f9fa175aa48>
140323585370696
C++ address....... PySide.QtCore.QThread/0xe3d880
hasOwnership...... 0
containsCppWrapper 0
validCppObject.... 1
wasCreatedByPython 0
方法我们称之为线程的 parent 。每当删除对象时(例如,最后调用thread()
的对象),包装器也会被删除。确实非常可疑的行为;我不确定它是否是一个bug,但这证明了信任包装对象的.thread()
根本不可信任。
答案 1 :(得分:2)
我认为在此上下文中使用QThread.currentThread()
并期望获得有意义的结果是无效的。
正如我所理解的那样,QThread不是一个线程,而是一个线程的包装器,我们看到的只是为工作线程重用的包装器。
如果我将QThread.currentThread()
替换为QCoreApplication.instance().thread()
,我会得到正确的输出:
<PySide.QtCore.QThread object at 0x00000000029D98C8>
<PySide.QtCore.QThread object at 0x00000000029D9908>
in thread <PySide.QtCore.QThread object at 0x00000000029D98C8> message in main thread
in thread <PySide.QtCore.QThread object at 0x00000000029D9908> message in worker thread
这背后的原因非常简单,前一个调用没有保留对QThread包装器的引用,不像QCoreApplication.instance().thread()
确实保留了引用。
我们可以通过修改原始样本来验证这一点:
main_thread = QtCore.QThread.currentThread() # holds the reference
print(main_thread)
# give us a thread and start it
thread = QtCore.QThread()
print(thread)
现在输出:
<PySide.QtCore.QThread object at 0x00000000028EB888>
<PySide.QtCore.QThread object at 0x00000000028EB8C8>
in thread <PySide.QtCore.QThread object at 0x00000000028EB888> message in main thread
in thread <PySide.QtCore.QThread object at 0x00000000028EB8C8> message in worker thread
编辑:
通过重复使用包装器,我的意思是存储第一个包装器的内存块被第二个包装器重用,这就是包装器具有相同内存地址的原因。
但是我很感兴趣,这背后的机制是什么。原因是因为Python对小对象使用 PyMalloc (默认情况下从Python 2.3开始启用),但也可能完全是glibc分配器本身的副作用(如果是现代分配器,如jemalloc或tcmalloc)和python使用标志--without-pymalloc
)编译: