有没有办法在使用后台回调时更快地更新plotly/dash?

0 投票
0 回答
43 浏览
提问于 2025-04-12 01:45

下面的代码使用了一个后台回调(plotly/dash)来获取一个值。

在这里输入图片描述

每隔3秒,update_event(event, shared_value)会设置一个event并定义一个value。与此同时,listening_process(set_progress)在等待这个event

当等待时间比较短(比如几秒钟)时,这个方法似乎运行得很好。但是当等待时间低于1秒(比如500毫秒)时,listening_process(set_progress)就会漏掉一些值。

有没有办法让后台回调更新得更快呢?看起来服务器的更新有几百毫秒的延迟,这和我设置event的频率无关。

import time
from uuid import uuid4
import diskcache
import dash_bootstrap_components as dbc
from dash import Dash, html, DiskcacheManager, Input, Output
from multiprocessing import Event, Value, Process
from datetime import datetime
import random


# Background callbacks require a cache manager + a unique identifier
launch_uid = uuid4()
cache = diskcache.Cache("./cache")
background_callback_manager = DiskcacheManager(
    cache, cache_by=[lambda: launch_uid], expire=60,
)

# Creating an event
event = Event()

# Creating a shared value
shared_value = Value('i', 0)  # 'i' denotes an integer type

# Updating the event
# This will run in a different process using multiprocessing
def update_event(event, shared_value):
    while True:
        event.set()
        with shared_value.get_lock():  # ensure safe access to shared value
            shared_value.value = random.randint(1, 100)  # generate a random integer between 1 and 100
        print("Updating event...", datetime.now().time(), "Shared value:", shared_value.value)
        time.sleep(3)
        
app = Dash(__name__, background_callback_manager=background_callback_manager)

app.layout = html.Div([
    html.Button('Run Process', id='run-button'),
    dbc.Row(children=[
        dbc.Col(
            children=[
                # Component sets up the string with % progress
                html.P(None, id="progress-component")
            ]
        ),
    ]
    ),
    html.Div(id='output')
])

# Listening for the event and generating a random process
def listening_process(set_progress):
    while True:
        event.wait()
        event.clear()
        print("Receiving event...", datetime.now().time())
        with shared_value.get_lock():  # ensure safe access to shared value
            value = shared_value.value  # read the shared value
        set_progress(value)


@app.callback(
    [
     Output('run-button', 'style', {'display': 'none'}),
     Output("progress-component", "children"),
    ],
    Input('run-button', 'n_clicks'),
    prevent_initial_call=True,
    background=True,
    running=[
        (Output("run-button", "disabled"), True, False)],
    progress=[
        Output("progress-component", "children"),
    ],
)
def run_process(set_progress, n_clicks):
    if n_clicks is None:
         return False, None  
    elif n_clicks > 0:
        p = Process(target=update_event, args=(event,shared_value))
        p.start()
        listening_process(set_progress)
        return True, None 
 
if __name__ == '__main__':
    app.run(debug=True, port=8050)

编辑:我找到了一种使用interval的解决方案。

@app.callback(
    [
     Output('run-button', 'style', {'display': 'none'}),
     Output("progress-component", "children"),
    ],
    Input('run-button', 'n_clicks'),
    prevent_initial_call=True,
    interval= 100, #### **THIS IS NEW**
    background=True, 
    running=[
        (Output("run-button", "disabled"), True, False)],
    progress=[
        Output("progress-component", "children"),
    ],
)

但是,这种方法相比于dcc.interval有什么优势呢?在这两种情况下,都是在轮询。唯一的优势似乎是,消费者在值被生成的确切时刻就能收到更新的值。

0 个回答

暂无回答

撰写回答