7 ms·
>Every unit of concurrency in the application domain can be represented by its own thread, making programming concurrent applications easier. Funny, I have nev
by stonemetal12 4y ago
>Every unit of concurrency in the application domain can be represented by its own thread, making programming concurrent applications easier.
Funny, I have never heard anyone describe threads as the thing that made concurrent programming easier, or more threads would make it easier.
What makes concurrent programming easier is thread interaction models that make it hard to lock the process.
async\await, give you concurrency without the need to for the developer to explicitly write lock code. Much easier than having to manually handle that interaction.
- cogman10 4y ago> async\await, give you concurrency without the need to for the developer to explicitly write lock code. async\await in no way relieve the developer from needing to worry about locks. If you have shared mutable memory, you have locks. If you aren't considering that, then you've got broken code. > What makes concurrent programming easier is thread interaction models that make it hard to lock the process. Yes, the way you do that is by focusing on message passing and immutable data when you can get away with it and, ideally, prebuilt thread safe datastructures when you can't. What async\await buys is lightweight threading when lightweight threading isn't available. It allows you to have millions of concurrent processes running at the same time. HOWEVER, the cost of this is the "colored function" problem. You HAVE to mark up your code to let the compiler/framework know "this is code that can block and thus needs to be able to give up control and resume". This problem is difficult because you can't simply call an async function from a non-async function. You have to do some wrapping/juggling to make everything play nice. The reason lightweight threading is nice is because you no longer have the colored function problem. From any reference or context you can say `CompletableFuture.supplyAsync(()->calculateValue())` and have a new concurrent action spawned. If that action does IO or whatever, it doesn't hog a thread, it just yields (like async/await does) and lets another task move forward. The only reason for async/await is OS threads are expensive and have pretty large negative implications on operating systems.
- SemanticStrengh 4y agoJS async is lock free though.
- ackfoobar 4y agoYou will need locks if you need mutual exclusion across an `await`.
- cogman10 4y agoJS has an uber lock. JS has a strict requirement that only one thread can run javascript code. That means that whenever you get to that `await` block, you've effectively established a whole application lock which prevents other awaits from proceeding (until your code yields). In otherwords JS isn't lock free, it's got 1 really big lock that covers all user code. If you really wanted to, you could simulate this behavior in threaded languages by having a global lock you acquire before you run anything. It's the python GIL problem [1] Languages like kotlin, rust, C++, or C# with async/await MUST concern themselves with locks because they don't acquire whole application locks anytime they run a sliver of code. [1] https://realpython.com/python-gil/ https://realpython.com/python-gil/
- SemanticStrengh 4y agoGreat explanation in general except web workers are a thing and sharedarraybuffer allow shared memory between them. Thats why js has atomics. Although while js can have conçurent races via shared memory, it doesn't expose locks IIRC? If so that's bad
- cogman10 4y agoWeb workers are certainly weird... and it's generally best to ignore them :D But if you don't, then yeah, you sort of run right into the need for an actual lock since you can have shared memory in concurrently executing interpreters. Entire: Web Lock [1] Specifically designed to resolve this issue. In typical JS programming this is not an issue, but if you are trying to really abuse that javascript VM then this is the way you'd do it. [1] https://developer.mozilla.org/en-US/docs/Web/API/Web_Locks_API https://developer.mozilla.org/en-US/docs/Web/API/Web_Locks_A...
- jayd16 4y ago>async\await in no way relieve the developer from needing to worry about locks. If you have shared mutable memory, you have locks. This isn't really true. For example, UI code often uses async/await for the concurrency but a single UI thread to prevent locking. Because this design is only concurrent and not parallel you don't need to worry about thread safety while still getting a way to yield execution to other code.
- pmoleri 4y agoI think it depends on the language. What you say is true for JS because it's single threaded but .Net has a thread pool, that means that you can have concurrent and even parallel tasks attending different events from your single thread UI.
- jayd16 4y agoYou cannot use a single UI thread in a parallel way. Its a contradictory statement. Parallel is the opposite of single threaded. C# does have a thread pool that you can use with async/await with multiple threads but that would be different from a UI thread or its synchronization context.
- pmoleri 4y agoIt's not contradictory, the UI is single threaded but on the dispatched events you can perfectly have long running tasks that can perform parallel work and affefct common memory. It's also my understanding that if you stick to async/await and don't create your own tasks, the synchronization context will be the one of the UI thread thus never run in parallel. However, this doesn't prevent you from doing so.
- jayd16 4y agoAh I see what you mean. Sure, you can do that if you desire. My point was simply that the system allows you to not do that. It's easy to get concurrency without parallelism. And it's also easy to get parallelism. But yes, In C# you're only guarded by self control and the slightly cumbersome calls it takes to jump threading context. It's not hard to stay in a single context but it's not enforced.
- Nullabillity 4y ago> async\await in no way relieve the developer from needing to worry about locks. If you have shared mutable memory, you have locks. If you aren't considering that, then you've got broken code. The huge difference is that futures (and thus async/await) change the primary concurrency concept you think about into "wait until this value is available" rather than managing the locks and shared memory yourself. The latter is still available (and still as footgunny as ever) but aren't as much of a problem when better options are more ergonomic. Futures also compose far better than threads do, especially when you want to wait for "any of X" rather than "all of X".
- pron 4y agoI don't understand the distinction you make between futures and threads. Virtual threads make working with futures more pleasant.
- cogman10 4y ago> The huge difference is that futures (and thus async/await) change the primary concurrency concept you think about into "wait until this value is available" rather than managing the locks and shared memory yourself. Java has had Futures for a while (and pretty good ones since Java 8's addition of "CompletableFuture") That being said, the biggest issue with Java Futures has not been the futures themselves, but rather the management of the ThreadPools for when you want to do IO in the futures. As pron points out, virtual threads make that WAY better to work with.