为什么不建议在广播中启动一个线程做任务

转:http://blog.youkuaiyun.com/kingyc123456789/article/details/53818606


首先需要知道:
1,这里所说的广播是静态广播
2,广播是运行在ui线程,阻塞15s会ANR
3,当android程序第一次运行时会启动一个进程和一个ui线程(不考虑多进程,多线程等)

一个未运行的程序接受到一个广播后的流程:

1,启动程序进程
2,启动ui线程和调用广播的 onReceive(Context context, Intent intent)方法
3,onReceive方法返回后,进程和线程处于等待状态;系统任意时刻可以终止和回收该线程和进程占有资源(内存)

如果:在onReceive开启一个线程做任务,任务未做完系统有可能回收进程,做任务的线程就没有对应的进程上下文,会被回收,任务就有可能未完成。

所以建议在onReceive中启动一个服务(再在服务中开线程做任务)做长时间任务。这样程序进程为后台进程(没有使用startForeground)系统就不会任意时刻回收此进程

<think>我们正在讨论如何在Python中启动一个WebSocket后台线程,涉及异步编程和并发处理。根据引用[1]和[2],我们推荐使用`asyncio`和`websockets`库来实现异步WebSocket服务。但是,用户要求将WebSocket服务放在“后台线程”中运行,这意味着我们需要在一个单独的线程中运行异步事件循环。关键点:1.异步WebSocket服务器通常运行在asyncio事件循环上。2.事件循环默认在主线程中运行,但我们可以将其运行在后台线程中。解决方案:我们可以创建一个线程,在该线程启动一个事件循环,然后在该事件循环中运行WebSocket服务器。步骤:a.定义一个后台线程,在该线程中创建并运行事件循环。b.在事件循环中启动WebSocket服务器。c.主线程可以继续执行其他任务。注意:由于asyncio事件循环通常在每个线程中只能有一个,并且我们无法在线程中直接调用事件循环,因此我们需要在目标线程中创建事件循环。示例代码:首先,安装必要的库:```bashpipinstallwebsockets```然后,编写代码:```pythonimportasyncioimportthreadingimportwebsockets#WebSocket服务器处理函数asyncdefhandle_client(websocket,path):asyncformessageinwebsocket:print(f"Received:{message}")awaitwebsocket.send(f"Echo:{message}")#启动WebSocket服务器的协程asyncdefrun_websocket_server():#创建WebSocket服务器,绑定到本地8765端口server=awaitwebsockets.serve(handle_client,"localhost",8765)print("WebSocketserverstartedatws://localhost:8765")#等待服务器关闭(实际上会一直运行直到被取消)awaitserver.wait_closed()#在后台线程中运行事件循环的函数defstart_websocket_server_in_thread():#创建一个新的事件循环loop=asyncio.new_event_loop()asyncio.set_event_loop(loop)#运行服务器协程loop.run_until_complete(run_websocket_server())#创建并启动后台线程websocket_thread=threading.Thread(target=start_websocket_server_in_thread,daemon=True)websocket_thread.start()#主线程继续执行其他任务print("Mainthreadisrunning...")try:#例如,主线程可以等待用户输入或其他任务whileTrue:user_input=input("Type'exit'tostop:")ifuser_input.strip().lower()=='exit':breakexceptKeyboardInterrupt:passfinally:print("Shuttingdown...")#由于我们设置了daemon=True,当主线程退出时,后台线程也会自动结束(或者我们可以通过事件循环的停止机制来优雅关闭)#但这里我们使用daemon线程,所以主线程退出程序就结束。如果需要更优雅的关闭,可以使用事件循环的cancel或stop机制,但这里展开。print("Done.")```说明:1.我们创建了一个新的线程(websocket_thread),并将其设置为守护线程(daemon=True),这样当主线程退出时,后台线程也会自动退出。2.在后台线程中,我们创建了一个新的事件循环,并设置为此线程的当前事件循环。3.然后在该事件循环中运行WebSocket服务器协程(run_websocket_server)。4.主线程可以继续执行其他任务,比如等待用户输入。注意事项:-由于异步操作必须在同一个线程的事件循环中运行,所以我们将整个事件循环都放在后台线程中。-守护线程的设置使得主线程退出时,后台线程会立即终止,这可能导致连接未正常关闭。如果需要更优雅的关闭,我们可以通过向事件循环发送停止信号来实现(例如使用loop.call_soon_threadsafe来安排一个停止任务),但这样会更复杂。引用[1]中提到,在Python3.7+中推荐使用`asyncio.run()`,但注意`asyncio.run()`只能用于主线程,并且一个线程只能调用一次。因此,我们在后台线程能直接使用`asyncio.run()`,而是使用`loop.run_until_complete()`。引用[3]和[4]强调了协程适用于I/O密集型任务,而WebSocket服务器正是这样的场景。这样,我们就实现了在后台线程中运行WebSocket服务器,而主线程可以继续执行其他任务。注意:由于GIL的存在,Python的多线程在CPU密集型任务上并能提高性能,但这里我们主要处理I/O,且异步编程可以高效处理大量并发连接,因此是合适的。如果需要停止服务器,我们可以通过取消事件循环中的任务来实现,但上面的示例中只是简单地让主线程等待用户输入,然后退出(同时守护线程也会退出)。如果需要更复杂的控制,例如在主线程中控制服务器的停止,我们可以使用线程间通信机制(如Event)或者通过向事件循环发送停止任务(使用`loop.call_soon_threadsafe`)来安全地停止服务器。这里我们提供一种改进的停止方法(可选),但为了简单,上面的例子使用了守护线程。改进的停止方法(供参考):1.在后台线程的事件循环中,我们可以将服务器对象保存起来,然后提供一个停止函数,该函数通过`loop.call_soon_threadsafe`来安排一个停止服务器的任务(例如调用server.close(),然后等待server.wait_closed())。2.主线程在需要停止时调用这个停止函数。但考虑到复杂性,这里展开,用户可以根据需要扩展。以上就是在Python中启动WebSocket后台线程的方法。生成相关问题:1.如何优雅地停止运行在后台线程中的WebSocket服务器?2.在多线程环境中使用asyncio需要注意哪些问题?3.WebSocket服务器如何处理高并发连接?4.守护线程和非守护线程在Python中有什么区别?</think>### Python WebSocket 后台线程的异步实现 在 Python 中实现 WebSocket 后台线程需要结合异步编程(`asyncio`)和多线程技术。以下是完整实现方案: #### 核心方案 ```python import asyncio import threading import websockets import signal # WebSocket 消息处理协程 async def handle_client(websocket): async for message in websocket: print(f"收到消息: {message}") await websocket.send(f"已处理: {message}") # WebSocket 服务器协程 async def websocket_server(): async with websockets.serve(handle_client, "localhost", 8765) as server: print("WebSocket 服务器已在后台启动: ws://localhost:8765") await asyncio.Future() # 永久运行直到取消 # 启动后台线程的函数 def start_background_websocket(): # 创建新的事件循环 loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) # 启动服务器 loop.run_until_complete(websocket_server()) # 启动事件循环 loop.run_forever() # 优雅停止函数 def stop_background_websocket(loop, thread): loop.call_soon_threadsafe(loop.stop) thread.join() print("WebSocket 后台服务已停止") # 主程序入口 if __name__ == "__main__": # 创建并启动后台线程 websocket_thread = threading.Thread( target=start_background_websocket, daemon=True ) websocket_thread.start() print("主线程继续运行...") # 注册优雅退出 loop = asyncio.get_event_loop() signal.signal(signal.SIGINT, lambda s, f: stop_background_websocket(loop, websocket_thread)) # 主线程执行其他任务 try: while True: user_input = input("输入 'exit' 停止服务: ") if user_input.strip().lower() == 'exit': stop_background_websocket(loop, websocket_thread) break finally: print("程序退出") ``` #### 关键实现原理 1. **线程与事件循环分离** - 主线程使用默认事件循环 - 后台线程创建**独立事件循环** `asyncio.new_event_loop()`[^1] - 通过 `asyncio.set_event_loop()` 设置线程专属循环 2. **守护线程机制** - `threading.Thread(daemon=True)` 确保主线程退出时自动终止后台线程[^4] - 避免僵尸线程问题 3. **异步持久化运行** - `loop.run_forever()` 保持事件循环持续运行[^3] - `await asyncio.Future()` 实现永久阻塞直到外部取消 4. **优雅停止设计** - `loop.call_soon_threadsafe(loop.stop)` 线程安全停止循环 - `thread.join()` 等待线程完成退出 - 信号处理 `signal.signal()` 捕获 Ctrl+C[^1] #### 应用场景 1. **实时数据监控**:后台接收设备数据,主线程处理业务逻辑 2. **消息推送服务**:保持长连接推送通知,影响主程序 3. **微服务架构**:作为独立通信模块嵌入现有系统 #### 性能优化建议 1. **连接池管理**:使用 `websockets.broadcast` 实现高效广播 2. **心跳检测**:添加 `ping_interval` 和 `ping_timeout` 参数 ```python websockets.serve(..., ping_interval=30, ping_timeout=10) ``` 3. **协议优化**:使用二进制消息替代文本减少传输开销 4. **资源限制**:设置 `max_size` 防止大消息攻击 ```python websockets.serve(..., max_size=2**20) # 限制1MB ``` #### 注意事项 1. **线程安全**:避免直接跨线程操作 WebSocket 对象 2. **GIL 影响**:CPU 密集型任务应考虑使用 `multiprocessing`[^4] 3. **错误处理**:添加异常捕获防止线程崩溃 ```python try: loop.run_forever() except Exception as e: print(f"事件循环异常: {e}") finally: loop.close() ``` > 此实现符合 Python 异步编程最佳实践[^1],通过线程隔离解决了异步服务与主程序协同工作的问题[^3],同时保持 WebSocket 的高并发特性[^2]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值