Changing session state not reflecting in active python thread

Hello Everyone,

I’m trying to write an app that plots continually changing data (potentially many plots in parallel) using threads and session state to avoid rerunning some code on widget change. The following code does this WITHOUT threading, but restricts me to having only one plots:

from time import sleep as snooze
import streamlit as st
from streamlit import session_state as state
import numpy as np

def change_func():
    state.periodic_function = state.periodic_function_dict[state.func_choice]


st.title("Animator")

plot_here = st.empty()

if 'periodic_function_dict' not in state:
    state.periodic_function_dict = {"cos(x)": np.cos, "sin(x)":np.sin}
if 'n' not in state:
    state.n = 1001
if 'x' not in state:
    state.x = np.linspace(0, 2*np.pi, state.n)
if 'index' not in state:
    state.index = 0

st.radio("Choose a function to plot.", state.periodic_function_dict.keys(), index=0, horizontal=True, key='func_choice', on_change=change_func)
if 'periodic_function' not in state:
    change_func()

while True:

    with plot_here.container():
        st.line_chart(state.periodic_function(state.x + state.x[state.index % state.n]), use_container_width=True)
        st.write(state.index)
        st.write(state.periodic_function)

    state.index += 1

    snooze(.001)

To allow me to make multiple plots and also create app elements that appear after the plot, I packaged the loop into a function that I run using python threads:

from time import sleep as snooze
import streamlit as st
from streamlit import session_state as state
from streamlit.runtime.scriptrunner import add_script_run_ctx as ctx
import numpy as np
import threading

def change_func():
    state.periodic_function = state.periodic_function_dict[state.func_choice]

def plot_loop():

    while True: 

        with plot_here.container():
            st.line_chart(state.periodic_function(state.x + state.x[state.index % state.n]), use_container_width=True)
            st.write(state.index)
            st.write(state.periodic_function)

        state.index += 1

        snooze(.001)

st.title("Animator")

plot_here = st.empty()

if 'periodic_function_dict' not in state:
    state.periodic_function_dict = {"cos(x)": np.cos, "sin(x)":np.sin}
if 'n' not in state:
    state.n = 1001
if 'x' not in state:
    state.x = np.linspace(0, 2*np.pi, state.n)
if 'index' not in state:
    state.index = 0

st.radio("Choose a function to plot.", state.periodic_function_dict.keys(), index=0, horizontal=True, key='func_choice', on_change=change_func)
if 'periodic_function' not in state:
    change_func()

if "loop_thread" not in state:
    state.loop_thread = threading.Thread(target=plot_loop)
    ctx(state.loop_thread)
    state.loop_thread.start()

state.loop_thread.join()

However, any changes to the session state made outside of the thread are not reflected in the code that the thread is running. Is this not possible in streamlit, or am I making a mistake in setting this up? The documentation for using threads with streamlit seems to be a little thin on the ground.

Thanks in advance!

EDIT: I also checked that the session state inside of and outside of the thread are indeed the same using python’s id function.

Hey @JAGD,

I wanted to share some notes by @tim on multithreading from this related GitHub Issue:

None of Streamlit’s APIs are safe to call from any thread other than your script’s main thread; code that does so has ventured into the realm of undefined behavior, and we strongly recommend against it :slight_smile:

If you need to run a separate thread that invokes Streamlit APIs, you’ll need to do something like pushing requests to a shared queue that’s processed by your script thread.

(This isn’t something that’s particularly easy to do with Streamlit, unfortunately. But we do have some - still vague - plans to make more complex use cases, like this one, less tricky!)

Based on the above, it seems like what you’re seeing is unfortunately expected behavior. Agreed that we should document this more publicly for reference.

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.