如何构建固定更新值的Streamlit仪表盘?解决asyncio重复追加问题
解决方案
解决思路
- 固定指标不重复追加:用
st.empty()创建占位容器,后续仅更新容器内的st.metric数值,避免不断新增元素导致页面无限拉长。 - 异步任务后台持续运行:单独启动线程运行asyncio事件循环,让两个worker任务脱离Streamlit会话生命周期,即使无浏览器访问,任务也能在后台持续执行。
完整代码示例
import streamlit as st import asyncio import threading import time # 全局变量存储指标值,用锁保证多线程读写安全 val_one = 0 val_two = 0 lock = threading.Lock() # 定义异步worker任务 async def worker_one(): global val_one while True: with lock: val_one += 1 await asyncio.sleep(1) # 每秒更新一次指标一 async def worker_two(): global val_two while True: with lock: val_two += 2 await asyncio.sleep(1.5) # 每1.5秒更新一次指标二 # 启动后台异步任务的线程函数 def run_async_tasks(): loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) # 同时运行两个worker任务 loop.run_until_complete(asyncio.gather(worker_one(), worker_two())) # 仅首次运行时启动后台线程,避免每次刷新页面重复创建线程 if "background_thread_started" not in st.session_state: thread = threading.Thread(target=run_async_tasks, daemon=True) thread.start() st.session_state["background_thread_started"] = True # 创建两个固定的占位容器 metric_one_holder = st.empty() metric_two_holder = st.empty() # 循环更新指标显示 while True: # 加锁读取当前值,避免多线程读写冲突 with lock: current_val1 = val_one current_val2 = val_two # 更新占位容器内的metric元素 metric_one_holder.metric(label="指标一", value=current_val1) metric_two_holder.metric(label="指标二", value=current_val2) time.sleep(0.5) # 控制页面刷新频率 st.rerun()
重点注意事项
- 线程安全:必须用
threading.Lock()保护全局变量,防止后台线程与Streamlit主线程同时读写导致数据异常。 - 任务不重复启动:借助
st.session_state标记线程已启动,避免用户刷新页面时重复创建线程,导致任务多实例运行。 - 页面刷新控制:
time.sleep(0.5)可根据需求调整页面刷新间隔;st.rerun()负责触发页面刷新,展示最新的指标数值。
运行验证
在Windows 10环境下执行streamlit run app.py:
- 浏览器中仅显示两个固定的指标卡片,数值持续更新,页面不会无限变长。
- 关闭浏览器后,后台worker任务仍会继续运行(可在worker函数中添加
print()语句验证),重新打开浏览器能看到指标值已累计增长。
内容的提问来源于stack exchange,提问作者bbartling
相关产品推荐
相关产品推荐

