为什么要用子进程
- 在pyqt中。在做复杂运算的时候、为了让主界面不卡顿。通常我们会启动一个工作线程、用来处理耗时的操作、然后子线程将运行结果通过信号的方式发给主线程、主线程做UI的更新
- 但是这种方法只适用于I/0密集型、假设工作线程运行的为CPU密集型计算、那么在进程资源直接被占满了。就算用了子线程。UI更新依然会被卡住。
- 所以针对运算CPU密集型计算。只能是让UI更新在主进程里面运行、并额外创建一个子进程用于计算、进程之间的通信可以考虑使用pipe管道。为了并发处理。考虑在子进程里面用线程池颁发多个线程同时运算
- 当然针对I/O密集型的操作。不需要用到子进程。用于进程的创建本身就需要耗费一些资源。反而会慢、
- 下面是demo代码
import concurrent.futures
import multiprocessing
import sys
import loguru
import rawpy
from PIL import Image
from PySide2.QtCore import Slot, Signal, QThread
from PySide2.QtWidgets import QApplication, QMainWindow, QPushButton, QLabel, QWidget, QVBoxLayout
class SubProcess:
"""子进程"""
def __init__(self, parent_conn):
self.parent_conn = parent_conn
def process_files(self, dng_files):
"""在子进程里面启用线程池、提高并发处理能力"""
with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:
processed_files = []
def process_and_notify(input_file, output_file):
"""
处理文件并发送通知
"""
self.parent_conn.send(["processing", input_file])
result = self.process_file(input_file, output_file)
return result
for input_file, output_file in dng_files.items():
processed_files.append(executor.submit(process_and_notify, input_file, output_file))
for future in concurrent.futures.as_completed(processed_files):
try:
status, result = future.result()
if status == "success":
self.parent_conn.send(["success", result])
else:
self.parent_conn.send(["fail", result])
except Exception as e:
self.parent_conn.send(["fail", str(e)])
self.parent_conn.send(["ClosePipe", ''])
self.parent_conn.close()
def process_file(self, input_file, output_file):
"""
CPU密集计算逻辑函数
"""
try:
with rawpy.imread(input_file) as raw:
rgb = raw.postprocess(use_camera_wb=True, half_size=True, no_auto_bright=False, output_bps=8)
image = Image.fromarray(rgb)
image.save(output_file, quality=85)
return "success", output_file
except Exception as e:
return "fail", str(e)
class Worker(QThread):
"""主进程里的子线程用于和子进程的pipe通信"""
data_received = Signal(tuple)
def __init__(self, parent_conn):
super().__init__()
self.parent_conn = parent_conn
def run(self):
while True:
if self.parent_conn.poll():
status, path = self.parent_conn.recv()
if status == "ClosePipe":
break
self.data_received.emit((status, path))
class MainWindow(QMainWindow):
"""
主进程
"""
def __init__(self):
super().__init__()
central_widget = QWidget()
self.setCentralWidget(central_widget)
layout = QVBoxLayout()
self.button = QPushButton("启动子进程")
self.label = QLabel("子进程正在运行")
layout.addWidget(self.button)
layout.addWidget(self.label)
central_widget.setLayout(layout)
self.button.clicked.connect(self.startSubProcess)
@Slot()
def startSubProcess(self):
parent_conn, child_conn = multiprocessing.Pipe()
sub_process = SubProcess(child_conn)
dng_files = {
r"D:\相机样片\尼康人像 - 副本.NEF": r"D:\相机样片\1.jpg",
}
self.process = multiprocessing.Process(target=sub_process.process_files, args=(dng_files,))
self.process.start()
self.worker = Worker(parent_conn)
self.worker.data_received.connect(self.updateUI)
self.worker.start()
loguru.logger.info('开始执行')
@Slot(tuple)
def updateUI(self, data):
status, path = data
self.label.setText(path)
loguru.logger.info(status)
loguru.logger.info(path)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())