Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

In the actor model you can have non-determinism. Two actors are going to send you messages to update the state in a third. Depending on which message arrives first, you can arrive at a different final state in the third actor. And which can arrive first depends on scheduling issues that can change between runs. That's non-deterministic, and it's part of what makes multi-threading so hard.

The fork-join model doesn't have this problem because you can only join a determinate number of tasks, and you have to join them in a determinate order. You can't have anything arriving at any other order than you specified. Scheduling issues cannot change how a fork-join program runs.

In the actor model you also have a really complicated state situation. You've got n actors, all each in a possibly infinite number of states. Because of the non-determinism described above the states your actors will get into in each run of the program can be different. That's another part of what makes multi-threading so hard.

The fork-join model doesn't have this problem because tasks don't have any state. They just have input and output. There's no state, only commutation.

The actor model is a big uncontrolled graph of state in my opinion. Fork-join is DAG of pure functional computation done in parallel.



Any code example?

In this model I think deadlocks is possible, like in CSP. I wonder if CSP is closer to this model, right?


No CSP is all about mutable state in each process.

Fork-join looks like, where we blur an image with two-way parallelism:

   a, b = divide image into halves
   fork {
     a' = blur a
     b' = blur b
   }
   result = combine halves a' b'
So all the tasks in the fork { } block run in parallel. The task that created those tasks stops and waits for them to all finish and to have all their results before it continues.

And there's no way the order of the tasks finishing can change the result because you always have to wait for all your tasks to finish.

See how there's no mutable state? You can only start a task, and accept its result. You can't see or change anything in between that.

You can get a deadlock if one task crashes. Some people really care about that, some people see it as an implementation detail and wouldn't really define it as a 'deadlock' in the same way.


So, you basically have what JS calls await Promise.all(a, b) ?


Yes you can build fork-join that way, but you'd need to remember to manually follow some rules. You should create the tasks a and b immediately before you await, and do no other work between creating them and awaiting. And you should not cause any side effects in the two tasks.

Systems which have fork-join built-in would either help you to follow those rules, or force you to.


So, if I understand, you "tasks" are almost pure functions:

   fork -> send -> task1(input) & task2(input) -> joiner
And you must always(?) have a join? so I can't have a zombie task elsewhere?

I probably like how this sound! Exist a more detailed material to study on this?




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: