4 ms·
If I had a dollar for every time I heard "thread programming is hard." I've programmed using threads for 23 years. I've never had a non-trivial debug issue cau
by cjensen 9y ago
If I had a dollar for every time I heard "thread programming is hard."
I've programmed using threads for 23 years. I've never had a non-trivial debug issue caused by trouble using semaphores, mutexes, and shared data. It's no harder than writing a hash table or balancing a tree.
- julian_1 9y agoDo you have proofs for your code that it can't deadlock or livelock? How strong are your claims - would you trust it in a life support system, or aircraft auto-pilot, or similar role?
- cjensen 9y agoNope. Nor do I have any proofs that my balanced binary tree implementation is correct, or that my hash implementation is correct. Given the number of stories about equator issues and negative altitude issues in autopilots, I'm unconvinced there are proofs involved in those either.
- julian_1 9y agoI would trust my own multi-threaded code too, but it requires much stronger knowledge of expected behaviors and code paths. I have had the misfortune to try and debug other's multithreaded code. Having a debugger connected to a production app, while waiting a week for it to deadlock is tedious.
- YZF 9y agoIt's easier when you restrict yourself to: - take mutex - read/write/modify shared data structure - release mutex There are plenty of multi-threaded RTOS and a lot of critical embedded software is multi-threaded. Typically there are clearer guarantees in those vs e.g. Linux. Multi-threaded code isn't fundamentally (from a theoretical perspective) different than single threaded code when it's running on a single core. Multiple cores communicating is sort of orthogonal.
- oconnor663 9y agoI think Niko Matsakis addresses this well in his talk at C++Now: https://youtu.be/lO1z-7cuRYI https://youtu.be/lO1z-7cuRYI It's reasonable for one person to write a correct multi-threaded program, even a large one. But when many different people start working on the same large, multi-threaded program, correctness becomes much more difficult. People start forgetting which functions and data structures are and are not threadsafe.
- BigJono 9y ago> People start forgetting which functions and data structures are and are not threadsafe. That sounds like something which could easily be solved with common sense commenting of code.
- runT1ME 9y ago> I've never had a non-trivial debug issue caused by trouble using semaphores, mutexes Sure, those are easy, but they don't scale. You'll often want non-blocking instead of semaphores for higher levels of parallelism and thats where you'll run into trouble.
- YZF 9y agoSame here. You just stick to a few simple patterns that work. If you have a shared data structure you protect that with a mutex. You don't hold the mutex and call other functions. You take the mutex, you read or write the shared structure, and you release the mutex. That's the basic pattern. People run into deadlocks and other issues when they don't realize what mutex protects which data, or they try to take multiple mutexes, or they hold a mutex while calling a callback, or they don't protect access at all. From a performance perspective you need to be aware of how much contention you have on a mutex. Typically locking a mutex should never block. If you have contention there are patterns for dealing with that as well. For most types of concurrent applications you're done. Yeah, lockless techniques, spin locks, etc. may have their place but for the most part keep it simple.
- Matthias247 9y agoI have much less professional programming experience. Nevertheless I saw: - Dozens of examples where access to a variable shared-between threads was not synchronized. These can be nasty, because often you won't observe an issue at all. And maybe some month or years later weird issues are reported, which can often not be reproduced and where the root-cause is hard to find. - Deadlocks, due to locks taken in different orders. E.g. in cases where a user-thread with user-code calls in a library and the library calling back into user code from another thread. - Nasty memory corruption issues. E.g. there was a case where one started a timer (which executed in the background thread) which should manipulate some variable when the timer elapses. In case the timer was no longer needed it was cancelled. Under some race conditions the code in the timer thread ran even after it was cancelled (from the user thread) and accessed memory which was already freed in the main thread. The correct solution would have been in the main thread to wait until the timer has guaranteed to stop (with out without executing it's code) and only to release the memory afterwards. This happened in an embedded system where you get no segfault but just weird behavior. So it was definitely a non-trivial to debug issue on which multiple days were wasted. - In event-based systems code which blocks the whole eventloop (and thereby slows down the system immensely) by waiting for the result of a long-running operation. E.g. in .NET code you might often find people just using Task.Result without knowing it's implications (or what the difference to await Task is). My personal experience is that while for some people concurrency and multithreading is quite easy there are also lots of people which have a hard time understanding and utilizing it correctly.