An actor is conceptually like a lock: it protects some data from data races by only allowing one bit of code to access it at a time. That basic abstraction can be implemented in a number of ways.
One traditional way to implement it is to have the actor be a sort of job queue; when you want the actor to do something, you just add a job to the actor's queue, and you make sure there's a thread that's going to process the queue. The actor might have a thread dedicated to it, always waiting around for something to be added to its queue, in which case you just have to wake that thread up; or it might not, meaning you need to ask the system for a thread to come run jobs for the actor. In either case, some thread runs jobs from the actor queue one after another, usually until it's drained the queue completely, and then goes back to either wait for more jobs or do something else.
That traditional implementation is heavily biased towards preserving locality for the data and code of the actor and biased away from preserving locality for the data and code of the tasks that want to use the actor. That's great if you have an actor that's heavily contended, because there'll be a single thread that processing all the actor's jobs, and the CPU will naturally keep the actor's data and functions in cache on whatever core is running that thread, allowing all the actor operations to complete very quickly. But it's pretty bad if you have an actor that's lightly contended, because it's quite likely that you'll do a thread switch every time you either start or finish a request on the actor, and the locality for all the data and code associated with your task can be very poor.
The idea of "switching" is that the thread follows a single task as much of possible. An actor still has a job queue. If a task makes a request of an actor, then instead of immediately adding an asynchronous job to that queue, the task asks to start executing the actor on the current thread. Now, that can fail, most likely because there's already a thread running the actor; if it does, then the task just adds itself as a job to the actor's queue, like it would in the traditional queue implementation. But if it succeeds, then the current thread can immediately go and run code on behalf of the actor, for basically no more cost than a single atomic operation. And on the other side, when the task is done running code on the actor, it just asks the actor to record that there's no longer a thread processing it, and then the thread can immediately continue running whatever code comes next for the task. If the actor has more jobs to do, it just asks the system for a thread to continue processing it. (As a result, when there is significant contention for the actor, actor processing can end up bouncing between threads, which isn't great. I expect that we'll want to do some work to recognize this kind of actor contention dynamically and have the thread stick with the actor for awhile. But low contention is very common.)
Another way of looking at this is that the actor has an "asynchronous lock". A traditional synchronous mutex has a queue of waiting threads built into it, and when a thread fail to acquire the lock, it blocks on that queue until the lock is available. This is basically exactly that, except that it's a queue of jobs instead of threads, and so waiting never blocks a thread, it just blocks a task by adding a job to resume it to the queue.