使用 omp 的 CPython 扩展冻结了 Qt UI
CPython extension using omp freezes Qt UI
我正在研究一种科学算法(图像处理),它是用 C++ 编写的,使用大量并行化,由 OpenMP 处理。我需要它可以从 Python 调用,所以我创建了一个 CPython 包,它处理算法的包装。
现在我需要一些 UI,因为用户交互对于初始化某些东西是必不可少的。我的问题是当我 运行 算法时 UI 冻结。我在一个单独的线程中启动算法,所以这应该不是问题(我什至通过用 time.sleep
替换函数调用来证明它,并且它工作正常,不会导致任何冻结)。为了进行测试,我将 UI 减少为两个按钮:一个用于启动算法,另一个只是将一些随机字符串打印到控制台(以检查 UI 交互)。
我也经历了一些非常奇怪的事情。如果我开始移动鼠标,然后按下按钮开始计算,然后继续移动鼠标,UI 不会冻结,所以将鼠标悬停在按钮上会给他们通常的蓝色 Windows风格的色调。但是,如果我在应用程序 window 上停止移动鼠标几秒钟,单击一个按钮,或切换到另一个 window,UI 会再次冻结。更奇怪的是,如果我将鼠标放在应用程序 window.
之外,UI 仍保持活动状态,这是我的代码(不幸的是,由于多种原因我无法共享该算法,但我希望即使这样我也设法得到了一些帮助):
if __name__ == "__main__":
from PyQt5.QtWidgets import QApplication, QPushButton, QVBoxLayout, QWidget
from PyQt5.QtCore import QThread, QObject, pyqtSignal
import time
from CustomAlgorithm import Estimator # my custom Python package implemented in C++
class Worker(QObject):
finished = pyqtSignal()
def run(self):
estimator = Estimator()
estimator.calculate()
# if the above two lines are commented, and the next line is uncommented,
# everything's fine
# time.sleep(5)
print("done")
app = QApplication([])
thread = QThread()
window = QWidget()
layout = QVBoxLayout()
# button to start the calculation
btn = QPushButton("start")
layout.addWidget(btn)
btn.clicked.connect(thread.start)
# button to print some text to console
btn2 = QPushButton("other button")
layout.addWidget(btn2)
btn2.clicked.connect(lambda: print("other button clicked"))
window.setLayout(layout)
# handling events
worker = Worker(app)
worker.moveToThread(thread)
thread.started.connect(worker.run)
worker.finished.connect(thread.quit)
worker.finished.connect(worker.deleteLater)
thread.finished.connect(thread.deleteLater)
window.show()
app.exec_()
我尝试了多种使用线程的变体,例如 threading.Thread
、multiprocessing.Process
、PyQt5.QtCore.QThread
(如上所示),甚至是 napari
的 worker 实现,但是结果是一样的。我什至尝试从代码中删除 omp,以防它以某种方式干扰 python 线程,但它没有帮助。
至于我使用python的原因是最终目标是让我的实现在napari
中可用。
非常感谢任何帮助!
由于Python的“全局解释器锁”,一次只有一个线程可以运行 Python编码。但是,其他线程可以同时做I/O。
如果你想允许其他线程 运行(就像 I/O 那样)你可以用这些宏包围你的代码:
Py_BEGIN_ALLOW_THREADS
// computation goes here
Py_END_ALLOW_THREADS
其他 Python 个线程将被允许 运行 在进行计算时。您无法从这两行之间的 Python 访问 任何内容 - 因此请按顺序 在 Py_BEGIN_ALLOW_THREADS
之前获取数据。
我正在研究一种科学算法(图像处理),它是用 C++ 编写的,使用大量并行化,由 OpenMP 处理。我需要它可以从 Python 调用,所以我创建了一个 CPython 包,它处理算法的包装。
现在我需要一些 UI,因为用户交互对于初始化某些东西是必不可少的。我的问题是当我 运行 算法时 UI 冻结。我在一个单独的线程中启动算法,所以这应该不是问题(我什至通过用 time.sleep
替换函数调用来证明它,并且它工作正常,不会导致任何冻结)。为了进行测试,我将 UI 减少为两个按钮:一个用于启动算法,另一个只是将一些随机字符串打印到控制台(以检查 UI 交互)。
我也经历了一些非常奇怪的事情。如果我开始移动鼠标,然后按下按钮开始计算,然后继续移动鼠标,UI 不会冻结,所以将鼠标悬停在按钮上会给他们通常的蓝色 Windows风格的色调。但是,如果我在应用程序 window 上停止移动鼠标几秒钟,单击一个按钮,或切换到另一个 window,UI 会再次冻结。更奇怪的是,如果我将鼠标放在应用程序 window.
之外,UI 仍保持活动状态,这是我的代码(不幸的是,由于多种原因我无法共享该算法,但我希望即使这样我也设法得到了一些帮助):
if __name__ == "__main__":
from PyQt5.QtWidgets import QApplication, QPushButton, QVBoxLayout, QWidget
from PyQt5.QtCore import QThread, QObject, pyqtSignal
import time
from CustomAlgorithm import Estimator # my custom Python package implemented in C++
class Worker(QObject):
finished = pyqtSignal()
def run(self):
estimator = Estimator()
estimator.calculate()
# if the above two lines are commented, and the next line is uncommented,
# everything's fine
# time.sleep(5)
print("done")
app = QApplication([])
thread = QThread()
window = QWidget()
layout = QVBoxLayout()
# button to start the calculation
btn = QPushButton("start")
layout.addWidget(btn)
btn.clicked.connect(thread.start)
# button to print some text to console
btn2 = QPushButton("other button")
layout.addWidget(btn2)
btn2.clicked.connect(lambda: print("other button clicked"))
window.setLayout(layout)
# handling events
worker = Worker(app)
worker.moveToThread(thread)
thread.started.connect(worker.run)
worker.finished.connect(thread.quit)
worker.finished.connect(worker.deleteLater)
thread.finished.connect(thread.deleteLater)
window.show()
app.exec_()
我尝试了多种使用线程的变体,例如 threading.Thread
、multiprocessing.Process
、PyQt5.QtCore.QThread
(如上所示),甚至是 napari
的 worker 实现,但是结果是一样的。我什至尝试从代码中删除 omp,以防它以某种方式干扰 python 线程,但它没有帮助。
至于我使用python的原因是最终目标是让我的实现在napari
中可用。
非常感谢任何帮助!
由于Python的“全局解释器锁”,一次只有一个线程可以运行 Python编码。但是,其他线程可以同时做I/O。
如果你想允许其他线程 运行(就像 I/O 那样)你可以用这些宏包围你的代码:
Py_BEGIN_ALLOW_THREADS
// computation goes here
Py_END_ALLOW_THREADS
其他 Python 个线程将被允许 运行 在进行计算时。您无法从这两行之间的 Python 访问 任何内容 - 因此请按顺序 在 Py_BEGIN_ALLOW_THREADS
之前获取数据。