Many times, especially when working in a client-server model, you may want to perform operations “in the background”, i.e., send a batch of work to the server and continue with your local operation. At some later point, you may want to poll for the completion of the work, or perhaps be notified of its completion using a callback function.
RPyC is very well-suited for asynchronous work. In fact, the protocol itself is asynchronous, and synchronicity is layered on top of that – by issuing an asynchronous request and waiting for its completion. However, since the synchronous modus-operandi is the most common one, the library exposes a synchronous interface, and you’ll need to explicitly enable asynchronous behavior.
async_() takes any callable
netref and returns an asynchronous-wrapper around that netref.
When invoked, this wrapper object dispatches the request and immediately returns an
AsyncResult, instead of waiting for the response.
Create an async wrapper around the server’s
async_sleep = rpyc.async_(conn.modules.time.sleep)
And invoke it like any other function, but instead of blocking, it will immediately
res = async_sleep(5)
Which means your client can continue working normally, while the server
performs the request. There are several pitfalls using
async_, be sure to read the Notes section!
You can test for completion using
res.ready, wait for completion using
and get the result using
res.value. You may set a timeout for the result using
res.set_expiry(), or even register a callback function to be invoked when the
result arrives, using
The returns async proxies are cached by a weak-reference. Therefore, you must hold a strong reference to the returned proxy. Particularly, this means that instead of doing
res = async_(conn.root.myfunc)(1,2,3)
myfunc_async = async_(conn.root.myfunc) res = myfunc_async(1,2,3)
Furthermore, async requests provide no guarantee on execution order. In particular, multiple subsequent async requests may be executed in reverse order.
timed allows you to set a timeout for a synchronous invocation.
timed function is invoked, you’ll synchronously wait for the result, but no longer
than the specified timeout. Should the invocation take longer, a
AsyncResultTimeout will be raised.
Under the hood,
timed is actually implemented with
async_: it begins dispatches the
operation, sets a timeout on the
AsyncResult, and waits for the response.
# allow this function to take up to 6 seconds timed_sleep = rpyc.timed(conn.modules.time.sleep, 6) # wait for 3 seconds -- works async_res = timed_sleep(3) # returns immediately async_res.value # returns after 3 seconds # wait for 10 seconds -- fails async_res = timed_sleep(10) # returns immediately async_res.value # raises AsyncResultTimeout
Background Serving Thread¶
BgServingThread is a helper class that simply starts
a background thread to serve incoming requests. Using it is quite simple:
bgsrv = rpyc.BgServingThread(conn) # ... # now you can do blocking stuff, while incoming requests are handled in the background # ... bgsrv.stop()
BgServingThread allows your code (normally the client-side) to perform blocking
calls, while still being able to process incoming request (normally from the server). This allows
the server to send “events” (i.e., invoke callbacks on the client side) while the client is busy
doing other things.
For a detailed example show-casing the
BgServingThread, see Events in the