光伏追踪器Python脚本:解决Socket.accept()阻塞主循环的遥测方案
解决方案:解决Socket阻塞主循环的问题
针对你的光伏追踪器Socket遥测方案中accept()阻塞主循环的问题,有三种实用方案,按新手友好度排序:
1. 多线程方案(最适合新手,实现简单)
多线程完全可行,根本不算小题大做——它能让Socket监听在后台线程运行,主线程不受干扰地执行追踪器核心逻辑。只需把Socket服务封装成独立函数,用threading模块启动后台线程即可。
示例代码
import socket import threading import time # 共享遥测数据,写入时加锁保证线程安全(仅读取无需锁) telemetry_data = {"current_azimuth": 0, "wind_speed": 0.0} data_lock = threading.Lock() def run_socket_server(): # 初始化Socket server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.bind(('0.0.0.0', 12345)) # 绑定本地端口,OpenVPN内可访问 server_socket.listen(5) while True: conn, addr = server_socket.accept() # 读取最新遥测数据(加锁避免写入冲突) with data_lock: response = f"Azimuth: {telemetry_data['current_azimuth']}°, Wind Speed: {telemetry_data['wind_speed']} m/s" conn.send(response.encode()) conn.close() # 启动Socket服务线程(daemon=True表示随主线程退出而终止) socket_thread = threading.Thread(target=run_socket_server, daemon=True) socket_thread.start() # 追踪器主循环 try: while True: # 模拟追踪器核心逻辑:读取传感器、控制电机等 # 更新遥测数据时加锁 with data_lock: telemetry_data["current_azimuth"] += 1 # 示例:模拟朝向变化 telemetry_data["wind_speed"] = 2.5 + (telemetry_data["current_azimuth"] % 5) / 10 # 模拟风速 print(f"Running tracker, current azimuth: {telemetry_data['current_azimuth']}°") time.sleep(1) # 模拟主循环周期 except KeyboardInterrupt: print("Shutting down...")
2. 异步IO方案(单线程非阻塞)
如果不想用线程,可以用Python内置的asyncio实现异步Socket服务,主循环和Socket监听在同一个线程内交替执行,无需线程切换。需要把主逻辑改成异步模式(用await替代阻塞操作)。
示例代码
import asyncio telemetry_data = {"current_azimuth": 0, "wind_speed": 0.0} async def handle_client(reader, writer): # 生成遥测响应 response = f"Azimuth: {telemetry_data['current_azimuth']}°, Wind Speed: {telemetry_data['wind_speed']} m/s" writer.write(response.encode()) await writer.drain() writer.close() await writer.wait_closed() async def socket_server(): server = await asyncio.start_server(handle_client, '0.0.0.0', 12345) async with server: await server.serve_forever() async def tracker_main_loop(): while True: # 异步版追踪器核心逻辑 telemetry_data["current_azimuth"] += 1 telemetry_data["wind_speed"] = 2.5 + (telemetry_data["current_azimuth"] % 5) / 10 print(f"Running tracker, current azimuth: {telemetry_data['current_azimuth']}°") await asyncio.sleep(1) # 非阻塞延迟 async def main(): # 同时运行Socket服务和主循环 await asyncio.gather(socket_server(), tracker_main_loop()) if __name__ == "__main__": asyncio.run(main())
3. 非阻塞Socket+Select方案(嵌入现有主循环)
如果不想改动现有主循环结构,可以把Socket设置为非阻塞模式,用select模块检测连接请求,这样主循环不会被accept()阻塞。
示例代码
import socket import select import time # 初始化非阻塞Socket server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.bind(('0.0.0.0', 12345)) server_socket.listen(5) server_socket.setblocking(False) # 关键:设置为非阻塞 telemetry_data = {"current_azimuth": 0, "wind_speed": 0.0} try: while True: # 用select检测Socket是否有新连接(超时0.1秒,不阻塞主循环) readable, _, _ = select.select([server_socket], [], [], 0.1) for sock in readable: if sock is server_socket: conn, addr = server_socket.accept() response = f"Azimuth: {telemetry_data['current_azimuth']}°, Wind Speed: {telemetry_data['wind_speed']} m/s" conn.send(response.encode()) conn.close() # 追踪器主逻辑(和你原有代码完全兼容) telemetry_data["current_azimuth"] += 1 telemetry_data["wind_speed"] = 2.5 + (telemetry_data["current_azimuth"] % 5) / 10 print(f"Running tracker, current azimuth: {telemetry_data['current_azimuth']}°") time.sleep(1) except KeyboardInterrupt: server_socket.close() print("Shutting down...")
方案对比
- 多线程:新手友好,逻辑清晰,只需少量改动现有代码。注意遥测数据写入时加锁即可避免线程安全问题。
- 异步IO:单线程无锁,适合熟悉协程的开发者,需将主循环改为异步模式。
- 非阻塞Socket+Select:完全兼容现有主循环结构,无需额外线程/协程,代码稍复杂但对原有逻辑侵入性最低。
内容的提问来源于stack exchange,提问作者JanK
相关产品推荐
相关产品推荐

