Caller¶
Caller
is a class that makes it easy to call code in different threads/tasks.
One caller instance is created per thread, and each of those instances can be retrieved by name using the Caller.get_instance
class method or in the thread in which it is running simply by Caller()
.
Caller
is used by the kernel internally for running code, but can also be used directly by the user. Each caller starts its own iopub zmq socket.
Threads¶
The caller manages a pool of worker threads (not related to anyio worker threads) or you can specify the name of a new thread which you can manage yourself. Each thread has its own anyio event loop in which the code will be called.
Most methods that perform execution return an async_kernel.Future
.
Usage by the kernel¶
The kernel runs two threads; one each for the shell and control. The thread names are 'MainThread' and 'ControlThread' respectively. You can easily get the main thread caller by using the classmethod Caller.get_instance()
.
kernel uses one of Caller().call_soon
Caller.to_thread
depending on the header directive
Caller of the current thread¶
To get the caller for the current thread use Caller(). It will raise a Runtime error if the thread doesn't have a running instance.
To get the caller for the main thread.
Ensure you're running an async kernel.
%callers
Running Protected Name
──────────────────────────────────────────────────────────────────────
✓ 🔐 MainThread ← current thread
✓ 🔐 ControlThread
Example¶
This example requires ipywidgets!
import random
import time
import ipywidgets as ipw
from async_kernel import Caller
outputs = {}
def my_func(n):
caller = Caller()
if not (out := outputs.get(caller)):
outputs[caller] = out = ipw.HTML(description=str(caller))
out.style.description_width = "220px"
display(out)
sleep_time = random.random() / 4
out.value = f"{n=:04d} sleeping {sleep_time * 1000:03.0f} ms"
time.sleep(sleep_time)
return n
async def run_forever():
n = 0
while True:
n += 1
yield Caller.to_thread(my_func, n)
async for fut in Caller.as_completed(run_forever()):
result = await fut
print(f"Finished: {result}", end="\r")
HTML(value='', description='Caller<Thread-5 (anyio_run_caller)>', style=HTMLStyle(description_width='220px'))
HTML(value='', description='Caller<Thread-4 (anyio_run_caller)>', style=HTMLStyle(description_width='220px'))
HTML(value='', description='Caller<Thread-3 (anyio_run_caller)>', style=HTMLStyle(description_width='220px'))
HTML(value='', description='Caller<Thread-8 (anyio_run_caller)>', style=HTMLStyle(description_width='220px'))
HTML(value='', description='Caller<Thread-7 (anyio_run_caller)>', style=HTMLStyle(description_width='220px'))
HTML(value='', description='Caller<Thread-6 (anyio_run_caller)>', style=HTMLStyle(description_width='220px'))
HTML(value='', description='Caller<Thread-11 (anyio_run_caller)>', style=HTMLStyle(description_width='220px'))
HTML(value='', description='Caller<Thread-9 (anyio_run_caller)>', style=HTMLStyle(description_width='220px'))
HTML(value='', description='Caller<Thread-10 (anyio_run_caller)>', style=HTMLStyle(description_width='220px'))
HTML(value='', description='Caller<Thread-12 (anyio_run_caller)>', style=HTMLStyle(description_width='220px'))
Finished: 4
%callers
Running Protected Name
──────────────────────────────────────────────────────────────────────
✓ 🔐 MainThread ← current thread
✓ 🔐 ControlThread
✓ Thread-3 (anyio_run_caller)
✓ Thread-4 (anyio_run_caller)
✓ Thread-5 (anyio_run_caller)
✓ Thread-6 (anyio_run_caller)
✓ Thread-7 (anyio_run_caller)
✓ Thread-8 (anyio_run_caller)
✓ Thread-9 (anyio_run_caller)
✓ Thread-10 (anyio_run_caller)
✓ Thread-11 (anyio_run_caller)
✓ Thread-12 (anyio_run_caller)