use_thread
def use_thread(
callback=Union[
Callable[[threading.Event], T],
Iterator[Callable[[threading.Event], T]],
Callable[[], T],
Iterator[Callable[[], T]],
],
intrusive_cancel=True,
dependencies=[],
) -> Result[T]:
...
use_thread can be used to run medium or long running jobs in a separate thread to avoid blocking the render loop.
use_thread takes a function as argument that takes a threading.Event object as argument to check whether it should stop,
and should return it's resulting value, or yield it multiple times (thus an iterator).
The return value of use_thread is a Result[T], making it easy to have the UI reflect the state of the work being done.
When an iterator is passed, the state only enters the FINISHED state after the last element is yielded. Partial, or incremental results can be detected when the state is still in RUNNING, but Result[T].value is not None.
From any state the .cancel() or .retry() may be called, which will stop the current thread, and start all over.
Note that at no time, two threads will run, since the new thread will always wait for the previous to finish.
When intrusive_cancel=True, a tracer is installed into the thread, which will throw a special exception to cancel the thread. This gives some runtime overhead, and a more performant way would be to set this to false and manually inspect the threading.Event regularly. When this is not possible (e.g. usage of an external library), this is the only way to cancel a thread while executing.
Note that .value and .error of the result object will only be set when a new result is returned. This makes is possible for a component to render the "previous" result (or previous error) while the new job is running.
State diagram
The possible values for Result[T].state are stored in the dataclass solara.ResultState, with the possible options being
STARTINGWAITINGRUNNINGERRORFINISHEDCANCELLED
These states are reflected in the below state diagram
Future deprecation
Note that use_task is an alternative to use_thread that will most likely
replace use_thread in the future. Compared to use_thread, use_task has no intrusive cancel, which turned out to be too large of
a performance overhead to be useful. Also, use_task can run async functions, which is not possible with use_thread.