Open fosterbrereton opened 1 year ago
Aren't all your caveats actually caveats about various executors and not about actors at all?
Aren't all your caveats actually caveats about various executors and not about actors at all?
Yes, that's a great point. They have been brought up in the past talking about actors, though, so I thought they bore mentioning here if someone new to actors/executors is unaware of them.
actor<T>
provides asynchronous, serialized access to an instance ofT
, running on an execution context of choice. Instead of a traditional message-passing actor model implementation,actor<T>
is given work by way of lambdas, whose results are then optionally extracted by the caller via astlab::future<R>
.actor<T>
is a lightweight alternative to a dedicated thread managing some background service for a host application. The problem with background threads is that they consume considerable resources even when they are idle. Furthermore, many background services don't need the "always on" characteristics of a thread, and would be comfortable running only when necessary.However,
actor<T>
is not a panacea. There are several caveats to keep in mind:thread_local
variables may not retain state from task to task. Given the implementation details of the actor's executor (e.g., it may be scheduled on any number of threads in a thread pool), an actor may jump from thread to thread. Sincethread_local
variables have a per-thread affinity by definition, the variable values may change unexpectedly.(@dabrahams rightly observes that these issues are caveats about executors more than they are to actors.)
Example
Say we have a service,
type_rasterizer
, that we'd like to put on a background thread:In our application, then, we will create an actor that manages an instance of this engine. By giving it the
default_executor
, the actor will run on a thread of the OS-provided thread pool (e.g., GCD on macOS/iOS).Then as your application is running, you can send "messages" in the form of lambdas to this actor to perform serialized, asynchronous operations. Note the first parameter of the lambda is the
type_rasterizer
itself:You could also pass the argument to the lambda itself:
Note that the actor is not always running. That is, no threads are blocked on behalf of the actor while it waits for tasks to come in. Rather, the actor only schedules itself to run on its executor when it has work to do. Once the work is completed, the actor relinquishes the thread it is running on back to the executor. In this way, actors are considerably less resource-intensive than a dedicated worker thread to some background service.