Enable Automatic Threading#

Using threading in Panel can either be enabled manually, e.g. by managing your own thread pool and dispatching concurrent tasks to it, or it can be managed by Panel itself by setting the config.nthreads parameter (or equivalently by setting it with pn.extension(nthreads=...). This will start a ThreadPoolExecutor with the specified number of threads (or if set to 0 it will set the number of threads based on your system, i.e. min(32, os.cpu_count() + 4)).

Whenever an event is generated or a periodic callback fires Panel will then automatically dispatch the event to the executor. An event in this case refers to any action generated on the frontend such as the manipulation of a widget by a user or the interaction with a plot. If you are launching an application with panel serve you should enable this option configure this option on the CLI by setting --num-threads.

To demonstrate the effect of enabling threading take this example below:

import panel as pn

pn.extension(nthreads=2)

def button_click(event):
    print(f'Button clicked for the {event.new}th time.')
    time.sleep(2) # Simulate long running operation
    print(f'Finished processing {event.new}th click.')

button = pn.widgets.Button(name='Click me!')

button.on_click(button_click)

When we click the button twice successively in a single-threaded context we will see the following output:

> Button clicked for the 1th time.
... 2 second wait
> Finished processing 1th click.
> Button clicked for the 2th time.
... 2 second wait
> Finished processing 2th click.

In a threaded context on the other hand the two clicks will be processed concurrently:

> Button clicked for the 1th time.
> Button clicked for the 2th time.
... 2 second wait
> Finished processing 1th click.
> Finished processing 2th click.

Note

Note that the global ThreadPool is used to dispatch events triggered by changes in parameters, events (such as click events), defer_load callbacks and optionally onload callbacks.