5 ms·
Which is why you manage atomic access to non-thread-safe objects yourself, or use a thread-safe version of them when using them across threads. Multithreading
by Espressosaurus 2y ago
Which is why you manage atomic access to non-thread-safe objects yourself, or use a thread-safe version of them when using them across threads.
Multithreading errors are the worst to debug. In this case it's dead simple to identify at design time and warning flags should have gone up as soon as he started thinking about using any of the normal containers in a multithreaded environment.
- kachapopopow 2y agoTell that to inexperienced developers or making a massive single-thread project have multi-threaded capabilities.
- baggy_trough 2y agoMulti-threading - ain't nobody got time for that.
- mrkeen 2y agoYeah, our software politely waits for one customer to finish up with their GETs and POSTs before moving onto the next customer. We have almost one '9' of uptime!
- baggy_trough 2y agoThere are better ways than threading.
- mrkeen 2y agoYeah, like pretending you aren't
- baggy_trough 2y agoI don't know what you mean.
- saagarjha 2y agoasync/await, for example.
- mrkeen 2y agoIf you write: int balance = 0; int get {...} void set(int balance) {...} void withdraw(int amt) { if amnt <= balance { balance -= amnt; } } it will be flagged immediately in code review as a race condition and/or something that doesn't guarantee its implied balance>=0 invariant. Because threading. But lift the logic up into a REST controller like pretty much all web app backends: GET /balance/get POST /balance/set POST /balance/withdraw And it will sail straight through review. (Because we pretend each caller isn't its own Thread.)
- baggy_trough 2y agoYou're right, we can't get away from concurrency control with database, cache, even the file system. I'm still happy to never have to think about in-process multithreading bugs.
- stuff4ben 2y agoI've been that developer making a single-threaded app multi-threaded. Best way to learn though!
- BobaFloutist 2y agoEvery time I think I'm sorta getting somewhere in my understanding of how to write code I see a comment like this that reminds me that the rabbithole is functionally infinite in both breadth and depth. There's simply no straightforward default approach that won't have you running into and thinking through the most esoteric sounding problems. I guess that's half the fun!
- mrkeen 2y agoIt's not that bad. We just don't have the equivalent of GC for multi-threading yet, so the advice necessarily needs to be "just remember to take and release locks" (same as remembering to malloc and free). Hopefully someone will invent something like STM [1] in the distant year of 2007 or so [2]. It has actual thread-safe data structures. Not just the current choice between wrong-answer-if-you-dont-lock and insane-crashing-if-you-dont-lock. [1] https://www.adit.io/posts/2013-05-15-Locks,-Actors,-And-STM-In-Pictures.html https://www.adit.io/posts/2013-05-15-Locks,-Actors,-And-STM-... [2] https://youtu.be/4caDLTfSa2Q?feature=shared https://youtu.be/4caDLTfSa2Q?feature=shared
- LegionMammal978 2y agoRust takes pride in its 'fearless concurrency' (strict compile-time checks to ensure that locks or similar constructs are used for cross-thread data, alongside the usual channels and whatnot), while Go takes pride in its use of channels and goroutines for most tasks. Not everything is like the C/C++/C#/Java situation where synchronization constructs are divorced from the data they're responsible for.
- neonsunset 2y agoSynchronization primitives in Go are just as divorced as elsewhere, sometimes even more so - it does have channels, but Goroutines cannot yield a value, forcing you to employ a separate storage location together with WaitGroup/Mutex/RWMutex (which, unlike Rust's RWLock, is separate too, although C# lets you model it to an extent). This results in community developing libraries like https://github.com/sourcegraph/conc https://github.com/sourcegraph/conc which attempt to replicate Rust's Futures / C#'s Tasks.
- sunshowers 2y agoThe usual issue is code evolution over time, not the initial version which tends to be okay. You really want to have tooling strictly enforce invariants, and do so in a way that fails closed rather than open. In other words, use Rust.