As much as I hate to say it, I think Java probably has the best eaten cake implementation by far. Volatile makes sure variables stay sane on the memory side, if you only write to a variable from one thread and read it from others, then it just sort of magically works? Plus the executors to handle thread reuse for async tasks. I assume C# has the same concepts given that it's just a carbon copy with title case naming.
Python can't execute in on two cores at once, so it functionally has no multithreading, JS can share data between threads, but must convert it all to string because pointless performance penalties are great to have. Golang has that weird FIFO channel thing (probably sockets in disguise for these last two). C/C++ has a segfault.
> JS can share data between threads, but must convert it all to string
To be more precise, you can send data to web workers and worker threads by copying via the structured clone algorithm (unlike JSON this supports almost all data types), and you can also move certain transferable objects between threads which is a zero-copy (and therefore much faster) operation.
Ah yeah dataviews, but you still need to convert from json to those and that takes about as much overhead, plus they're much harder to deal with complexity-wise being annoying single type buffers and all. For any other language it would work better, but because JS mainly deals with data arriving from elsewhere it means it needs to be converted every single time instead of just maintaining a local copy for thread comms.
> Ah yeah dataviews, but you still need to convert from json to those and that takes about as much overhead
You don't necessarily need to have an intermediate JSON representation. Many of the built in APIs in Node and browsers return array buffers natively. For example:
const buffer = await fetch('foo.wav').then(res => res.arrayBuffer())
new Worker('worker.js').postMessage(buffer, [buffer])
This completely transfers the buffer to the worker thread, after which it is detached (unusable from the sending side) [1][2].
> Volatile makes sure variables stay sane on the memory side
This doesn't get you from shared-mutable-hell to shared-mutable-safe, it gets you from shared-mutable-relaxedmemorymodel-hell to shared-mutable-hell. It's the kind of hell you don't come across until you start being too smart for synchronisation primitives and start taking a stab at lockfree/lockless wizardry.
> if you only write to a variable from one thread and read it from others, then it just sort of magically works
I'm not necessarily convinced by that - but either way that's a huge blow to 'shared' if you are only allowed one writer.
> Plus the executors to handle thread reuse for async tasks.
What does this solve with regard to the shared-mutable problem? This is like "Erlang has BEAM to handle the actors" or something - so what?
Well it doesn't get you there because shared-mutable-safe doesn't exist, at least I doubt it can without major tradeoffs. You either err on the side of complete safety with a system that is borderline unusable for anything practical, or you let people do whatever they want and let them deal with their problems once they actually have them.
> either way that's a huge blow to 'shared' if you are only allowed one writer
Yeah for full N thread reading and editing you'd need N vars per var which is annoying, but that kind of every-thread-is-main setup is something that is exceedingly rare. There's almost always a few fixed main ones and lots running specific tasks that don't really need to know about all the other ones.
Python can't execute in on two cores at once, so it functionally has no multithreading, JS can share data between threads, but must convert it all to string because pointless performance penalties are great to have. Golang has that weird FIFO channel thing (probably sockets in disguise for these last two). C/C++ has a segfault.