Hi @prithivi, this is indeed possible but Processes are a bit more complicated than threads because they do not share memory and therefore your code and data needs to be serialized (by pickle
) in order for it to work. The example above can be modified to make this work but you need to
- define your intensive computation function (
blocking_function
) outside of theTestClass
otherwise cannot be pickled. I’m not sure why this happens exactly but I think its because its a subclass ofparam.Parameterized
. - Add the
if __name__ == '__main__':
check (at least on windows). This ensures the panel is only launched once for each process.
This example below should do what you want:
import param
import panel as pn
from concurrent.futures import ProcessPoolExecutor
from tornado.ioloop import IOLoop
import time
import numpy as np
executor = ProcessPoolExecutor(max_workers=2)
def blocking_function():
time.sleep(np.random.randint(1, 5))
return 5
class TestClass(param.Parameterized):
do_stuff = param.Action(lambda self: self._do_calc())
select = param.Selector(objects=range(10))
result = param.Number(0)
slider = param.Number(2, bounds=(0, 10))
text = param.String()
def __init__(self, **params):
super(TestClass, self).__init__(**params)
self.progress_widget = pn.widgets.Progress(active=True, value=0, align="center", sizing_mode="stretch_width")
self.completed_tasks = 0
self.num_tasks = 10
self.widgets = pn.Param(self.param)
self.col = pn.Column(*self.widgets)
self.col.append(self.progress_widget)
@param.depends('slider', watch=True)
def _on_slider_change(self):
# This functions does some other python code which we want to keep responsive
self.text = str(self.slider)
def _do_calc(self):
loop = IOLoop.current()
for i in range(self.num_tasks):
future = executor.submit(blocking_function)
loop.add_future(future, self.update)
def update(self, future):
number = future.result()
with pn.io.unlocked():
self.result += number
self.completed_tasks += 1
self.progress_widget.value = int(100 * self.completed_tasks / self.num_tasks)
def blocking_task(self):
# This function is not pickleable: Can't pickle local object 'Param.widget.<locals>.link'
time.sleep(np.random.randint(1, 5))
return 5
def panel(self):
return self.col
if __name__ == '__main__':
tc = TestClass()
pn.serve(tc.panel())