There's a few nuances to consider whenever deciding how to synchronize your concurrent code, be it using an actor, lock, or other patterns.
In your specific question "make an FIFO queue" this really falls into the "concurrent data-structures" (well, at least "concurrency-safe data-structures"), bucket and those usually prefer using locks or lock-free algorithms to provide the synchronization. Why? Because a data-structure is a low level concept that should provide highest performance and be reusable from most contexts. You would not want to thread hop in order to enqueue an element into a queue, that's too expensive.
So yes, for low level primitives, data-structures, and stuff like that locking (or lock-free algorithms) are the way to go usually.
Where does that leave actors? Well, for "normal day to day code" actors are a better choice. You don't really use an actor as "just a queue", sure, it "is" a queue but the value of an actor is in coupling state and logic -- this is my "Thingy Processing Actor", and all the thingy processing is inside it -- if someone wants to process a thingy, they ask the actor to do so. If they're both default actors, and the target wasn't running, we'll do efficient tricks to reuse the existing thread and not thread-hop etc -- so there's a lot of optimizations built into the model, but the core idea is about isolation by hopping.
The best thing about actors (and distributed actors) is that they're a great default for most day-to-day code, and the compiler will prevent you from writing all kinds of concurrency bugs when you rely on them. If you use locks manually, you'd have to be careful™ and think about the state much more. I also find that locking usually means that the state and logic are spread out across the codebase, and "random places" just grab the lock, modify some state, while with actors you would not do that -- by design, the "mutate the things" will be inside the actor -- which helps you organize your code into clean isolation domains, rather than have various pieces of code "randomly" reach for the lock, do some stuff, and write back the locked state...
You could abuse an actor "just as a lock" (actor State<T> { var s: T; func get() -> T; func set(_: T)), but that's somewhat missing the point -- you've not leveraged the actor semantically to have it be the "logical place where all my business logic sits".
So as much as it is a performance question:
- is the critical section very short, and we don't want to avoid thread hops, and taking a lock will be cheaper? (use lock)
- is it preferable to suspend if the code is already executing because it may take a while to complete and we wait but not block the calling thread? (use actor)
(And as always with performance questions: Trust no-one, benchmark everything.)
It very much is a design question as well:
- is this just a single state thing I'm swapping sometimes? (lock is fine)
- is this some complex business logic and I'd like to keep the logic and state encapsulated in one place, and get compiler to help me by encouraging me to put logic inside that place, rather than spread it all over the codebase? (use actor).
Those questions can help answer "what should I use?" I think. And an even shorter answer would be: default to actors, unless you have reasons not to. And one such reason could be that you're building a concurrent data-structure.