咨询:Python实现两个并行块循环的最优多线程方案
Python实现并行块循环的最佳方式
先结合你的场景说——从你贴的代码来看,你是在做串口数据读取这类I/O密集型任务,咱来聊聊几种可行的方案,以及哪种最适合你:
我有两个并行块循环,请问使用Python实现它们并行运行的最佳方式是什么?目前我正在通过以下程序试验多线程实现:
#!/usr/bin/env python import time import serial import os from threading import Thread ser = serial.Serial(port='/dev/ttyUSB0', baudrate=38400, timeout=None) ser.flushInput() ser.flushOutput() def getstrings(port): buf = bytearray() while True: b = port.read(1) if b == b'\x02': del buf[:] elif b == b'\x03': yield buf.decode...
1. 多线程(你的当前方案,最适合你的场景)
你的思路完全没问题!因为串口读取属于I/O阻塞操作,Python的GIL(全局解释器锁)在I/O等待的时候会自动释放,所以多线程能真正实现并行处理,而且开销小、实现简单。我给你完善下代码,把两个循环跑起来:
#!/usr/bin/env python import time import serial from threading import Thread ser = serial.Serial(port='/dev/ttyUSB0', baudrate=38400, timeout=None) ser.flushInput() ser.flushOutput() def getstrings(port): buf = bytearray() while True: b = port.read(1) if b == b'\x02': del buf[:] # 遇到帧头,清空缓存 elif b == b'\x03': try: data = buf.decode('utf-8') print(f"收到串口数据: {data}") buf.clear() except UnicodeDecodeError: print("数据解码失败,丢弃当前帧") buf.clear() else: buf.extend(b) # 累积数据 def second_parallel_loop(): # 这里写你的第二个并行循环逻辑,比如定时打印状态、处理其他I/O任务 count = 0 while True: print(f"第二个循环运行中,累计次数: {count}") count += 1 time.sleep(1) if __name__ == "__main__": # 启动两个子线程,设置daemon=True让它们随主线程退出 thread_serial = Thread(target=getstrings, args=(ser,), daemon=True) thread_second = Thread(target=second_parallel_loop, daemon=True) thread_serial.start() thread_second.start() # 让主线程保持运行,直到用户按Ctrl+C终止 try: while True: time.sleep(1) except KeyboardInterrupt: print("\n程序终止,关闭串口") ser.close()
这里补全了你没写完的解码逻辑,加了异常处理避免崩溃,还设置了守护线程让程序退出更优雅。
2. 多进程(适合CPU密集型任务)
如果你的两个循环里有大量计算(比如复杂的数据解析、数学运算),那多进程会更合适——它能绕过GIL,充分利用多核CPU的优势。不过对于串口这种I/O任务,多进程的开销比线程大很多,而且进程之间不能直接共享串口对象,得用进程间通信或者单独开串口,反而麻烦。简单示例如下:
from multiprocessing import Process # 函数定义和上面类似,注意进程间资源的问题 if __name__ == "__main__": p_serial = Process(target=getstrings, args=(ser,)) p_second = Process(target=second_parallel_loop) p_serial.start() p_second.start() p_serial.join() p_second.join()
除非你有CPU密集的需求,否则不推荐用这个方案。
3. Asyncio(异步I/O,适合高并发I/O场景)
如果你要同时处理很多I/O任务(比如多个串口、网络请求),异步I/O是更高效的选择——它用单线程实现并发,开销极小。不过需要你的串口库支持异步,比如用pyserial-asyncio这个第三方库:
import asyncio import serial_asyncio class SerialAsyncProtocol(asyncio.Protocol): def __init__(self): self.buf = bytearray() def data_received(self, data): self.buf.extend(data) # 处理帧头帧尾,和之前的逻辑一致 while b'\x03' in self.buf: frame_end_idx = self.buf.index(b'\x03') frame_data = self.buf[:frame_end_idx] # 找到帧头的位置 if b'\x02' in frame_data: frame_data = frame_data[frame_data.index(b'\x02')+1:] try: print(f"异步收到串口数据: {frame_data.decode('utf-8')}") except UnicodeDecodeError: print("异步解码失败") # 截断缓存,处理下一个帧 self.buf = self.buf[frame_end_idx+1:] async def second_async_loop(): count = 0 while True: print(f"异步第二个循环计数: {count}") count += 1 await asyncio.sleep(1) async def main(): loop = asyncio.get_running_loop() # 创建异步串口连接 transport, protocol = await serial_asyncio.create_serial_connection( loop, SerialAsyncProtocol, '/dev/ttyUSB0', baudrate=38400 ) # 同时运行第二个异步任务 await asyncio.gather(second_async_loop()) if __name__ == "__main__": asyncio.run(main())
这种方式适合高并发场景,但学习成本比线程高,如果只是两个简单的并行循环,线程完全够用了。
总结
结合你的串口读取场景,多线程是最佳选择——实现简单、开销小,完美适配I/O密集型任务的并行需求。如果后续有CPU密集的计算,再考虑多进程;如果要处理大量并发I/O,再尝试asyncio。
内容的提问来源于stack exchange,提问作者Ubaid
相关产品推荐
相关产品推荐

