[英]How to separately start and stop multiprocessing processes in Python?
我使用專用的 Python (3.8) 庫通過 USB 端口控制電機驅動器。
電機控制驅動器制造商(ODrive)提供的 Python 庫允許單個 Python 進程控制一個或多個驅動器。
但是,我想運行 3 個進程,每個進程控制 1 個驅動器。
在研究了各種選項(我首先考慮了虛擬機、Docker 容器和多線程)之后,我開始相信最簡單的方法是使用multiprocessing
。
我的問題是我需要一種方法來管理(即獨立啟動、監控和停止)多個進程。 其背后的實際原因是電機連接到不同的設置。 例如,如果出現故障,每個設置都必須能夠單獨停止和重新啟動,但其他正在運行的設置不應受到此操作的影響。
在閱讀了互聯網和 Stack Overflow 之后,我現在了解了如何創建處理Pool
、如何將進程與處理器內核關聯、如何啟動進程池以及排隊/加入它們(我不需要后者) .
我不知道如何獨立管理它們。 如何在不影響其他進程執行的情況下分別啟動/停止不同進程? 是否有庫來管理它們(甚至可能使用 GUI)?
您可以像這樣手動創建多個multriprocessing.Process
實例:
def my_func(a, b):
pass
p = multiprocessing.Process(target=my_func, args=(100, 200)
p.start()
並使用多處理原語Queue
、 Event
、 Condition
等對其進行管理。有關詳細信息,請參閱官方文檔: https://docs.python.org/3/library/multiprocessing.ZFC35FDC70D5FC69D269883A822E
在以下示例中,多個進程獨立啟動和停止。 Event
用於確定何時停止進程。 Queue
用於從子進程傳遞到主進程的結果。
import multiprocessing
import queue
import random
import time
def worker_process(
process_id: int,
results_queue: multiprocessing.Queue,
to_stop: multiprocessing.Event,
):
print(f"Process {process_id} is started")
while not to_stop.is_set():
print(f"Process {process_id} is working")
time.sleep(0.5)
result = random.random()
results_queue.put((process_id, result))
print(f"Process {process_id} exited")
process_pool = []
result_queue = multiprocessing.Queue()
while True:
if random.random() < 0.3:
# staring a new process
process_id = random.randint(0, 10_000)
to_stop = multiprocessing.Event()
p = multiprocessing.Process(
target=worker_process, args=(process_id, result_queue, to_stop)
)
p.start()
process_pool.append((p, to_stop))
if random.random() < 0.2:
# closing a random process
if process_pool:
process, to_stop = process_pool.pop(
random.randint(0, len(process_pool) - 1)
)
to_stop.set()
process.join()
try:
p_id, result = result_queue.get_nowait()
print(f"Completed: process_id={p_id} result={result}")
except queue.Empty:
pass
time.sleep(1)
我可能會做這樣的事情:
import random
import time
from multiprocessing import Process, Queue
class MotorProcess:
def __init__(self, name, com_related_params):
self.name = name
# Made up some parameters relating to communication
self._params = com_related_params
self._command_queue = Queue()
self._status_queue = Queue()
self._process = None
def start(self):
if self._process and self._process.is_alive():
return
self._process = Process(target=self.run_processing,
args=(self._command_queue, self._status_queue,
self._params))
self._process.start()
@staticmethod
def run_processing(command_queue, status_queue, params):
while True:
# Check for commands
if not command_queue.empty():
msg = command_queue.get(block=True, timeout=0.05)
if msg == "stop motor":
status_queue.put("Stopping motor")
elif msg == "exit":
return
elif msg.startswith("move"):
status_queue.put("moving motor to blah")
# TODO: msg parsing and move motor
else:
status_queue.put("unknown command")
# Update status
# TODO: query motor status
status_queue.put(f"Motor is {random.randint(0, 100)}")
time.sleep(0.5)
def is_alive(self):
if self._process and self._process.is_alive():
return True
return False
def get_status(self):
if not self.is_alive():
return ["not running"]
# Empty the queue
recent = []
while not self._status_queue.empty():
recent.append(self._status_queue.get(False))
return recent
def stop_process(self):
if not self.is_alive():
return
self._command_queue.put("exit")
# Empty the stats queue otherwise it could potentially stop
# the process from closing.
while not self._status_queue.empty():
self._status_queue.get()
self._process.join()
def send_command(self, command):
self._command_queue.put(command)
if __name__ == "__main__":
processes = [MotorProcess("1", None), MotorProcess("2", None)]
while True:
cmd = input()
if cmd == "start 1":
processes[0].start()
elif cmd == "move 1 to 100":
processes[0].send_command("move to 100")
elif cmd == "exit 1":
processes[0].stop_process()
else:
for n, p in enumerate(processes):
print(f"motor {n + 1}", end="\n\t")
print("\n\t".join(p.get_status()))
未准備好生產(例如,沒有異常處理,沒有正確的命令解析等),但顯示了這個想法。 有問題就大聲喊:D
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.