The Actor pattern, well, the 'proper' actor pattern, *everything* is an actor, from an 'object' to a single integer variable. They all communicate by passing messages (like telling an integer to add, and some number like 3, to do the equiv of "someInt+=3"), and the messages can cross pretty big boundaries, such as other processors, or even a network like the internet. I am not going to that extent, only will 'actor'ize higher level style things, going in a style of Stackless Python tasklets or Erlang processes (Erlang models it like an operating system, each little actor is like a process with the Erlang interpreter being the operating system, and they can communicate to other Erlang interpreters running on different cores or across the net, Erlang scales to an arbitrary amount of computers, and regardless of its functional language style, I would still use it if it was not utter hell to bind with and include with other apps). Internally messages can be routed four different ways:
- First, if the thing you are passing a message to is on the same operating system process, regardless of if it is on another thread or not, and it is currently not in a running state, just send the message along and let it handle it instantly, no wait time, no locking or anything needed, just call the function directly.
- Second, if the thing you are passing a message to is on the same operating system process, regardless of if it is on another thread or not, and it *is* in a running (this is the "if" check I mentioned earlier), then you can do one of two things, first, tasklet's are *not* supposed to run for an extended period, in the actor model it is generally decided that if you have an actor that is of any size or will take any amount of execution time, it is better to either split it into multiple actors, or use the scheduler to pause it and resume it at another timeslice, so you can either wait for it to be not in a running state (spin-wait, kind of like a lock, and this case should generally be rather exceptionally rare in a well made system), or you can stick it in some form of "Message Box" specific for the actor, usually some form of non-locking construct like an atomic CAS circular queue or list, problem is that for it to be 'best-speed' you need to have a predefined size, which causes a tasklet to take up extra memory, but you can make that on a per tasklet basis so one that may have contention issues (very *very* busy tasklets, like a router of some sort) you can give more memory, however if the message box queue is full, then you can either spin-wait or do a couple other things (there are a few options, up to the implementation).
- Third, if they are in another process, but still on the same computer, and it is a shared memory system (like any personal computer, x86/x64, powerpc, etc... based, not like CELL, the CELL is *not* a shared memory processor, each 'core' has its own memory, so you would need to use the fourth listed method here), then allocate some global memory (normally you would have a 'healthy' amount pre-allocated, depending on usage, it can be resizable during run as well, or you can have message spin-wait if too small, etc., but the global memory would store the messages, and you would tell some listener on the other processes that a message is available and which tasklet it goes to (tasklet are usually named, a string name can be useful, but normally you use a GUID, 128-bit is usually more then enough for tasklets to remain unique during a program run, but 256-bit or more is sometimes required for server type apps, usually you will 'expose' certain tasklets with a globally unique name across a process or even the entire 'universe' of systems so something can find a tasklet without its GUID needing to be explicitly passed to it) using the globally accessible memory.
- Fourth, if it is on another computer, on a non-shared memory system, or if you just do not want to use kernal memory, then you are down to UDP/TCP. Normally messages are always considered that they 'have' to arrive, the may arrive to a dead tasklet and thus get deleted without any warning, but they must arrive, so TCP is usually used, but TCP has too much overhead so UDP is usually better, just make some form reliability layer on it (will still be faster then TCP), and many good network libraries can be used for that automagically (RakNet is a good one for example). However, in my language, if a message is sent to another system you will be able to specify other details about it, like you do not need reliability then it will use normal UDP, so it may or may not arrive (if you specify that it does not always have to arrive, but it is a local message, but the tasklet is running and its queue is full, may just want to dump it instead of spin-waiting or whatever). The reason I am allowing messages to not need be reliable is that reliability is not always needed, think movement packets for a game person, it is fine if a drop a few in the middle because the movement will be interpolated anyway, you will want it reliable for when then 'stop' moving for sure, but it is fine to drop the occasional one while they are moving (by default reliability is needed, it is only when specified that you do not need it will it not be used, since the general case will be that reliability is required).
My app in the common case will know what message go where, so there will be no need for any checks beyond if the receiving tasklet is already running or not, but even then the check is not complicated as the GUID should hold all such location identifiable information, so a simple integer check is fine in the more complex case, then just calling the appropriate thing to handle the routing.
Problem with Javascript is that it does not have good primitives for suspending execution of something currently running and returning control to something else, while picking back up where it left off later, LUA and Stackless Python have good support for that (but LUA has poor messaging capabilities, and the coroutine switching cannot have a C function in the call stack, Stackless Python's message passing is quite nice, and it can have a C function in the call stack, but the switching becomes rather slow in that case due to a large memory copy, but they are both slower then heck in all cases), where-as my language is made specifically for it. I will still not allow C functions on the call stack, but that is not an issue if near everything is implemented in the language anyway (which is the reason I go all out, so to answer your question about just using it for specific things, I do use other scripting languages for very specific and limited things, but in this language it is better to put as much in it as possible, do note that in the general case this language will probably be faster then a C++ compiled language to do the same thing, you cannot say that about other scripting languages).
The thing about the actor model is that you do not need to think of threading like you normally do, like most engines that do threading at all will usually separate it, so the graphics will be in one thread, texture loading in another, AI in another or two, networking in another, and most everything else in another, and that is generally as fine-grained as it gets, but they all need to stay synced so there will be a lot of time that a cpu/core will be idle as a thread finishes and is waiting for data on everything else, wasted time, as well as the fact that once we get something like 64-core or more cpu's (*drool...*) that program is horribly inefficient since most of the cores are idle. I want my language to last me my life if it all possible, not just a few years or the life of the program I am making it for, and the best way to do that is for it to be as efficient as possible for any single core, and be able to scale to any number of cores or multiple machines. As you can see, this design is as finely threaded as you can get, because of that you have to think differently about how you program (do very little at a time, if anything takes too long, be sure to sleep or split it into other tasklets). I like the programming style, but I have gotten used to it due to Erlang and Stackless Python and it would take users to get used to it, but once they do it is better.
For note, the Actor model is designed to be like how 'real-life' handles things. Think about it though, the Object-Oriented model really does not map to anything, if you want information on something you cannot just directly get it, if you send some information in real-life you cannot be certain it is received without some sort of response. In essence, the Object-Oriented model is like the Actor model if all information transferred is perfectly reliable, which real-life is not, and neither is the Object oriented model (what happens if you try to call a member or access a variable on a class pointer when the pointer points to invalid memory, like zero, you get a nice segfault or something like that, and even testing the variable before hand, you cannot be certain that another thread has not already deleted it between the test and access lines, which is what evolved into things like requiring those costly locks and so forth). The Actor model is more like real-life in that if you send a message to something, like how I am sending a message to you here, or if we were speaking face-to-face, I can be reasonably sure you will get the message, but I cannot be certain; for example, this message, you may have died or something since the last time you messaged, hence you will never get this, you may still be alive even at the time I am typing this, but die a minute after I send, I could not have known about that, the time it takes the message to transfer also exists (just like in the tasklet world, you can send a message, but the tasklet may have died by the time it gets the message, or die before it gets the message, even after you send it, sounds like that segfault in an OO program, but in the Actor program, it just silently disappears). To be certain you got this message I will await either a response message (which could be just a message saying you got this, or could be more questions or what-not) and I would know for sure you got it, if there is no response I can still be reasonably sure you got it, but if I want to be certain then I can ask you to please respond saying you got it (thus inferring that you are also still alive) and since messages will eventually arrive (even after some unknown time), as long as I am alive (I do not care about the message if I am dead after all) I will receive it. Even when communicating face-to-face with someone, you cannot be sure they heard you (or even all of what you said), their mind may be wondering, they may not speak your language, you just cannot be absolutely certain unless they do something like recite what you said. Even going down to something more specific, take even light, the light you are seeing in the stars was a message sent about that star's status however many tens of thousands or millions of years ago, you do not know if it is even still alive or if it is a cloud of dust by now. If the OO model could apply to real-life, you would just be able to query it and instantly know an answer, that just cannot happen.
As for a direct answer to your last question, they can give a boost, if you use your time well, most games do not, their graphics or logic threads run too long and while the other threads wait for them to finish to get an updated state, they are doing nothing, wasting time.