ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

Brython Web Worker 实战指南:用 browser.webworker 在浏览器后台线程运行 Python

Brython Web Worker 实战指南:用 browser.webworker 在浏览器后台线程运行 Python 编程语言语言运行时编译器前端【免费下载链接】brythonBrython (Browser Python) is an implementation of Python 3 running in the browser项目地址https://gitcode.com/gh_mirrors/br/brython点击查看免费下载Web Worker 是浏览器提供的独立后台线程机制能让计算密集型的任务不阻塞主线程的界面响应。BrythonBrowser Python通过browser.webworker模块为 Python 脚本提供了与 Web Worker 的集成能力允许开发者用纯 Python 写出在主线程创建 Worker、在 Worker 中运行 Python 逻辑、通过消息或远程方法调用双向通信的完整方案。本文将围绕官方文档 webworker.md 展开结合仓库源码www/src/libs/_webworker.js、www/src/builtin_modules.js、www/tests/test_web_worker.py 等深入讲解读完本文后你将掌握基本WorkerParent/WorkerChild消息通信、RPCWorkerParent/RPCWorkerChild远程方法调用以及如何扩展出自定义 Worker 类型。一、为什么需要 browser.webworkerBrython 与 Web Worker 的结合点浏览器原生 Web Worker 用postMessage/onmessage进行通信脚本以独立的 JS 文件加载。Brython 让 Python 脚本也能跑在 Worker 里关键在于两点环境检测Brython 在启动时会判断当前是否运行在 Worker 中。在 www/src/brython.js 的开头部分约第 16 行可以看到判定逻辑$B.isWebWorker (undefined ! typeof WorkerGlobalScope) (function typeof importScripts) (navigator instanceof WorkerNavigator)模块差异化在 Worker 环境中browser模块的行为与主线程不同。查看 www/src/builtin_modules.js约第 105 行当$B.isWebWorker为真时模块会设置browser.is_webworker true删除window和win名字Worker 中只有self并把browser.self.send设为postMessage的别名同时document被替换成一个访问即抛ValueError(document is not available in Web Workers)的占位对象——这意味着 Worker 线程里无法操作 DOM。在这一底层能力之上Brython 提供了两条面向 Python 开发者的集成路径browser.worker以页面内script typetext/python classwebworker声明 Worker 脚本配合worker.create_worker()创建参见 webworker_mdn.html 与 worker.pybrowser.webworker本文的主角提供WorkerParent/WorkerChild消息通信与RPCWorkerParent/RPCWorkerChild远程方法调用两套 API。官方文档 webworker.md 明确指出browser.webworker提供的是基础的 Web Worker 集成用它以相对轻松的方式在 Web Worker 中运行 Python 脚本目前可用的有两组类基础Worker类与RPCWorker类。二、browser.webworker 核心对象与消息模型在使用前先建立整条连接两侧的角色认知对象运行位置职责WorkerParent主线程创建 Worker向子线程发送消息、接收回复WorkerChildWorker 线程接收父线程消息处理后回复RPCWorkerParent主线程像调用本地方法一样调用子线程中的 Python 方法RPCWorkerChildWorker 线程注册可被父线程远程调用的方法current_workerWorker 线程当前 Worker 内可用的WorkerChild或RPCWorkerChild实例Message两侧通用消息载体包含消息名name、数据data与消息 IDidS_RUNNING两侧通用Worker 状态常量表示 Worker 已启动并准备接收消息通信的底层单位是Message对象。它的构造形式为Message(name, data)例如ww.Message(ping, hi)。文档特别提醒Worker 从创建到真正就绪需要一段时间就绪标志是w.status ww.S_RUNNING不过这不影响你立即发消息——消息会被排队等 Worker 就绪后再按顺序发送。三、实战一基础 Worker 类消息驱动通信3.1 父线程侧创建 WorkerParent 并发送消息在页面主线程中用WorkerParent构造一个 Worker。构造函数接收三个参数脚本 URL要运行的 Python 脚本地址可以是绝对路径也可以是相对于 Brython 源码目录的相对路径脚本参数传给脚本的参数列表在 Worker 内部通过sys.argv读取环境变量一个字典在 Worker 内部通过os.environ读取。from browser import webworker as ww w ww.WorkerParent(web_workers/test_worker.py, [1, 2, 3], {test: Ahoj})接着用WorkerCommon.post_message发送消息。该方法第一个参数是Message实例第二个参数want_reply布尔值表示是否需要等待回复。若需要回复方法返回一个asyncio.Future当回复到达时 Future 会被解析resolve还可以传入第三个参数timeout秒超时后 Future 会以超时异常解析m ww.Message(ping, hi) r w.post_message(m, want_replyTrue) # r 是一个 asyncio.Futureawait/yield 它即可拿到回复如果不在乎回复或预期对方不会回复可以省略这两个参数w.post_message(ww.Message(quit, None))3.2 子线程侧实现 WorkerChild 的消息处理器Worker 脚本本身就是一个普通的 Brython Python 脚本。先导入所需对象再定义针对不同消息的处理器。注意Worker 环境里不能使用document但可以使用browser.console输出调试信息from browser.webworker import current_worker, Message from browser import console from sys import argv from os import environ def pong(self, message, **_): print(Web worker received message (, message.id, ), message.name, message.data) current_worker.post_reply(message, Message(pong, message.data)) def quit(self, *args, **kwargs): current_worker.terminate()这段代码里有三个关键 APIcurrent_worker.post_reply(message, reply)向父线程发送针对某条收到消息的回复。文档特别强调回复消息应当用post_reply而不是post_message以此表明这条消息是对已接收消息的应答current_worker.terminate()终止 Worker相当于关闭子线程current_workerWorkerChild实例仅在 Worker 线程内可用主线程中不存在文档原文此处表述为 in the main thread it is always结合上下文应为主线程中始终不可用使用时以实际环境为准。然后将处理器与消息名绑定WorkerCommon.bind_message的第一个参数是消息名第二个参数是消息到达时要调用的方法current_worker.bind_message(ping, pong) current_worker.bind_message(quit, quit)最后必须调用current_worker.exec()通知父线程我已就绪可以开始接收消息。否则父线程侧的消息会一直处于排队状态print(Starting test worker with args:, argv, and environment, environ) current_worker.exec()注意sys.argv与os.environ在此处正是构造WorkerParent时传入的参数与环境变量——它们从父线程穿越到了子线程的运行时环境。四、实战二RPC Worker 类远程方法调用基础WorkerParent/WorkerChild非常简洁适合直接搭积木。RPCWorkerParent和RPCWorkerChild就是官方给出的积木示例在不需要处理消息细节的场景里把 Worker 中的 Python 方法当作在主线程中运行一样直接调用。4.1 父线程侧等待就绪后远程调用与基础示例最大的不同是RPC 模式下必须真正等待 Worker 启动完成——因为只有 Worker 就绪后才知道它注册了哪些方法。所以整个流程要包进一个协程旧式写法用coroutineyield新式 Python 直接async defawait先wait_for_status(ww.S_RUNNING)等待就绪再调用远程方法from browser import webworker as ww from asyncio import coroutine coroutine def main(w): # Wait for the worker to start yield w.wait_for_status(ww.S_RUNNING) # Call the remote add method a yield w.add(10, 20) assert a 30 # Call the remote log method yield w.log(Test output) # Destroy the worker w.terminate() # Create a new instance of the worker w ww.RPCWorkerParent(web_workers/test_rpc.py, [1, 2, 3], {USER: nobody}) # Run the main method main(w)要点拆解w.wait_for_status(ww.S_RUNNING)等待 Worker 状态变为运行中返回后即可安全调用远程方法w.add(10, 20)远程方法调用。add必须定义在 Worker 中调用会返回一个asyncio.Future代表该方法调用的返回值在协程里yield或await它会暂停当前协程直到结果可用w.log(Test output)无返回值的远程调用同样适用w.terminate()销毁 Worker调用的是WorkerCommon.terminate。4.2 子线程侧注册可远程调用的方法RPC Worker 的实现与基础版非常相似唯一的差别是不再用bind_message绑定消息处理器而是用RPCWorkerChild.register_method注册可以被父线程远程调用的方法from browser.webworker import current_worker, Message from browser import console from sys import argv from os import environ def add(x, y): Adds two numbers return x y def log(*args): print(*args) # Register the add and log methods. current_worker.register_method(add) current_worker.register_method(log) # Tell the parent we are ready print(Starting test RPC worker with args:, argv, and environment, environ) current_worker.exec()register_method接收的是函数对象本身而非名字字符串注册后父线程即可通过同名属性调用。同样地脚本末尾必须调用current_worker.exec()向父线程广播就绪状态。值得注意虽然示例导入了Message但 RPC 模式下开发者通常不需要直接构造消息——方法的入参、出参的序列化与 Future 的解析都由框架在register_method/RPC 调用层完成。五、进阶创建自定义 Worker 类型文档指出WorkerParent/WorkerChild之所以设计得如此简单正是为了便于扩展。每一类新的 Worker 类型都需要成对实现两个类Parent 类在主线程中代表 Worker负责创建、发送请求、接收结果Child 类在 Worker 线程中代表 Worker负责处理请求、返回结果。两者通过 Parent 类上的CHILD_CLASS属性绑定它必须是一个字符串内容是在 Worker 中可以用于 import 出 Child 类的模块路径。例如RPCWorkerParent就把该属性设置为browser.webworker.RPCWorkerChild。创建后模块会自动在 Web Worker 中实例化 Child 类并把实例存入webworker.current_worker。因此你自定义的 Child 逻辑可以直接通过from browser.webworker import current_worker拿到实例进行注册与初始化。整个父类名 CHILD_CLASS 自动实例化的机制就是browser.webworker可扩展架构的核心。六、源码级支撑Brython 的 Worker 加载链路browser.webworker面向 Python 侧封装消息真正把 Python 脚本送进 Worker 并初始化 Brython 运行时的是底层 JavaScript 实现。理解这条链路有助于排查Worker 没起来消息没到之类的问题。6.1 Worker 脚本的注册与加载在页面中带有classwebworker的script typetext/python块或src指向的 Python 文件会被登记到$B.webworkers映射脚本名 → 脚本对象。见 www/src/brython_builtins.js约第 165 行// For all the scripts defined in the page as webworkers, mapping between // script name and its source code $B.webworkers {}加载侧的逻辑在 www/src/loaders.js 的ajax_load_script中当请求的脚本被标记为 web workers.is_ww时会把源码同时写入$B.webworkers[name]和$B.file_cache[filename]供后续创建 Worker 时取用VFS虚拟文件系统中的文件与 HTTP 请求两条路径都做了同样处理。6.2 运行时引导Blob URL importScripts真正的 Worker 创建逻辑在 www/src/libs/_webworker.js。其中create_worker()会用$B.py2js把 Python 源码编译成 JavaScript拼接一段引导头header其中按需列出 Brython 运行时脚本brython_builtins、py_ast、py2js、python_parser_peg_version等debug 级别大于 2 时逐模块加载否则直接加载压缩版brython并用importScripts()在 Worker 内加载将编译结果与引导头包进Blob通过URL.createObjectURL生成 Worker URLnew Worker(url)启动用 Promise 随机 token 做就绪握手Worker 内代码执行成功后会self.postMessage(ok_token)主线程据此 resolve Promise并触发onready回调执行出错则发送错误 token触发onerror。这与 webworker_mdn.html 中展示的用法对应主线程用worker.create_worker(worker, onready, onmessage)创建Worker 侧用bind(self, message)监听消息、用self.send(...)回发。仓库测试 test_web_worker.py 进一步验证了create_worker(test_web_worker, main, onmessage)的三种回调onready/onmessage/onerror以及结构化克隆数据列表、嵌套字典的往返正确性。6.3 相关的并发扩展仓库中还有一处与 Worker 相关的实现WebWorkerExecutorwww/src/Lib/concurrent/futures/webworker.py它参考 CPythonconcurrent.futures的进程池模型用submit(fn, *args) Future 的方式把任务投递到 Worker 执行并提供shutdown(waitTrue)管理生命周期。如果你在browser.webworker之上需要线程池式的并发抽象这是一个可直接阅读的参考实现。七、小结如何选择通信模式需求推荐模式关键 API发送自由消息、不关心回复基础 WorkerWorkerParentpost_message(msg)发消息并等待回复可设超时基础 Workerpost_message(msg, want_replyTrue, timeout...) Future像本地函数一样远程调用RPC WorkerRPCWorkerParentregister_methodwait_for_status扩展新的协议/Worker 类型自定义Parent/Child 成对实现 CHILD_CLASS属性无论选择哪种模式都有三条铁律Worker 内无法访问document主线程必须等待或允许排队Worker 就绪子线程最后一定要调用current_worker.exec()宣告就绪。掌握这些你就可以用纯 Python 在浏览器中轻松构建后台计算、图像处理、数据聚合等不阻塞界面的任务了。赞分享编程语言语言运行时编译器前端【免费下载链接】brythonBrython (Browser Python) is an implementation of Python 3 running in the browser项目地址https://gitcode.com/gh_mirrors/br/brython点击查看免费下载相关推荐Pyodide 使用指南在浏览器、Web Worker 与 Node.js 中运行 PythonPyodide 使用指南在浏览器、Web Worker 与 Node.js 中运行 Python Pyodide 是一个基于 WebAssembly 的 Py科学计算开发工具Brython实际应用案例研究Brython实际应用案例研究 本文通过多个实战案例深入探讨了Brython在Web开发中的实际应用涵盖了交互式数据可视化、实时聊天应用、任务管理看板、响应式编程语言语言运行时编译器前端Brython与Web Workers如何在浏览器中实现多线程Python编程Brython与Web Workers如何在浏览器中实现多线程Python编程 BrythonBrowser Python是一个在浏览器中运行Python编程语言语言运行时编译器前端上一篇BepInEx 完整安装与插件加载终极指南免费给 Unity 游戏加模组的快速方法下一篇开源工具破解3DS格式转换难题从CCI到CIA的完整技术指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表