neozhaoliang / surround-view-system-introduction

A full Python implementation for real car surround view system
MIT License
911 stars 316 forks source link

关于多线程的一些疑问 #12

Closed mutonix closed 4 years ago

mutonix commented 4 years ago
  1. 抱歉我对多线程不是很熟悉 为什么需要stop_mutex 和 processing_mutex 这两把锁呢? 我看到锁里面访问的都是对象的内部属性,也不是公共的部分 以及 每个线程都初始化了自己的 stopmutex 和 processing_mutex 两把锁 ,所以线程之间也不会因为这两把锁互斥 我不是很明白

  2. 抱歉我对qt也了解不多 我自己写QThread必须要先初始化QApplication才可以正常运行,否则它会说事件循环必须要QAplication支持 我查阅了qt5文档,它说重载run函数线程就不会使用事件循环,但我还是会以下报错 QEventLoop: Cannot be used without QApplication 但我发现好像你的代码好像并不需要QApplication,请问是怎么做到的呢?

真的十分感谢!

neozhaoliang commented 4 years ago

我当时写 stop 和 process 两把锁的主要目的是为了防止各个摄像头的图像内存被外部修改。我想的是如果在一个嵌入式平台开发商业版本的话,为了效率考虑要避免频繁分配和销毁内存,所以把对每种图像 (原始/校正/投影/鸟瞰) 放在一个全局的命名空间里面,各自使用一块固定的内存,使用 np.copyto 或者 cv::copyTo 来修改,这时候外部线程修改图像就是可能的了。

Python 里面的 queue 模块的 queue 是线程安全的,所以这个锁是多余的。主要是为了提醒迁移到 C++ 的 qt 时不要忘了这个锁。

我没有遇到你说的 qapplication 的问题,你可以贴出你的代码吗?

mutonix commented 4 years ago
  1. 明白了,虽然你的代码里面摄像头内存没有放在全局空间,但考虑到未来的使用所以加了锁是吗?

  2. QThread在使用中我遇到了一些以下疑问,以这个demo为例

import sys
from PyQt5.QtCore import QThread
import time

class Worker(QThread):

    def __init__(self):
        super().__init__()

    def run(self):
        for _ in range(10):
            print(_)
            time.sleep(1)

def main():
    qt1 = Worker()
    qt1.start()

if __name__ == '__main__':

    qt1 = Worker()
    qt1.start()
    #main()

2.1 首先 如果直接初始化和start()的话 就会报错QEventLoop: Cannot be used without QApplication 在这之前加上app = QApplication(sys.argv)就没事 2.2 我之后试着先把这两句放到main() 再运行(就像你的代码里的一样) 好像就不会报这个错误了 所以我估计可能在全局空间会它自己会初始化一个QEventloop,但是在局部的话就不会? 2.3 另外还有个小疑问, 如果主线程先结束了,qthread的线程也会自己停掉吗(根据我实验好像是这样。。)

真的十分感谢你!!!

neozhaoliang commented 4 years ago
  1. 是的。申请和释放图像内存空间需要频繁和操作系统进行交互,降低了效率。我认为应该使用固定的内存空间并在程序开始时进行一次性分配。 2.1. 你的初始化函数里面少了 parent=None 这个参数,否则 qt 认为线程的持有者是某个 Qapplication 的实例此句有误。 2.2 不清楚原因,可能是主线程被 qt 作为子线程的持有者了。 2.3 一般来说主线程结束未必导致子线程也随之结束,但是这里只有一个主进程,所以主线程结束,程序退出,自然子线程也就结束了此句多余
mutonix commented 4 years ago

请问是这样写吗

class Worker(QThread):

    def __init__(self, parent=None):
        super(Worker, self).__init__(parent)

    def run(self):
        for _ in range(10):
            print(_)
            time.sleep(1)

但我好像还是报错, 我是不是写错了

关于子线程退出的,我以前用python的threading创建的子线程好像它主线程退出了,子线程它自己还能继续,所以这个和Qthread不太一样?

neozhaoliang commented 4 years ago

我试了一下,不调用 main 函数,直接执行确实会报你说的错误,我之前是把主线程代码放在 main 函数里面,所以碰巧没有遇到的你的问题。这个我还不清楚具体原因,需要研究一下。

mutonix commented 4 years ago

如果把我那个demo用threading写的话

import sys
from threading import Thread
import time

class Worker(Thread):

    def __init__(self, parent=None):
        super(Worker, self).__init__(parent)

    def run(self):
        for _ in range(10):
            print(_)
            time.sleep(1)

def main():
    qt1 = Worker()
    qt1.start()

if __name__ == '__main__':
    main()

用threading就可以得到0到9的输出,请问这是主进程等待子线程结束才自己结束的吗 好像Thread分守护线程和非守护线程

用Qthread感觉好像就是主线程运行完main()直接结束,把子线程强行停止了,QThread则直接相当于Thread的守护线程?

neozhaoliang commented 4 years ago

我猜测是因为直接在 main 函数外部写的话,主线程会先结束,这时 worker 子线程还没有结束所以 Qt 会报这个错误。在 main 函数内部写的话,主线程会等待 main 函数退出以后才结束。实际上如果在你的代码后面加上 qt1.wait()等待子线程结束的话就不会报错。

mutonix commented 4 years ago

哦,确实是这样。

我前面还有个小问题 你可能没看到,就是关于threading和Qthread比较的那个

neozhaoliang commented 4 years ago

这个应该是 Qthread 和 Python 的 Thread 的行为不一致导致的。

mutonix commented 4 years ago

哦哦 好的我明白了 我的疑惑都得到了解答,真的非常非常感谢你,而且你的代码也给了我很大帮助 打扰你一下午了,实在不好意思。