Multi-threaded Programming Makes You Crazy?
gduranceau writes "Help! My program deadlocks! I got several concurrent threads that write the same variable! Everything goes well on my mono processor but becomes an incredible mess on that 16 CPU monster! And of course, as soon as I add traces, problems disappear... Don't panic! Calm down and take a deep breath. "
Oh wait. I was supposed to praise the NPTL tool, wasn't I. Um... well... it's very nice. And they've got... um... penguins on the homepage. Oh, and look! It's GPLed! Wow. Just... um... wow. Hey, did you know that the author of Minix wrote a book on OS Design? Really. It even covers the basics of multi-threading. It's pretty cool, you should... um... check it out. Yeah, that's the ticket!
Javascript + Nintendo DSi = DSiCade
It looks like it's just a link to a sourceforge project... was there supposed to be something else?
dennis
Are a little mad anyway ;)
Our greatest enemy is neither a single man, nor is it a nation, it is, as it has always been, our own greed.
I was expecting a bit more substance to the article than something that sounds like it came from a (bad) marking department (what's a "deep breathe" anyway?), and a link to some sourceforge project.
What's going on?
No, this title does. Is a Sentence? Is a Question? Why There a Space Before the Question Mark? What 'Programmation'?
I was wondering what was this program was about. Fortunately, here is there website. http://nptltracetool.sourceforge.net/
Ooo man the floppy drive is broken. No wait. The computer is just upside down.
Slashvertismentation.
These words are fillers.
Whaaaaaaaaaa?
Then read slashdot!
"Programmation" isn't even a word.
"Take a deep breathe?"
No thanks, I think I'll go loose my mind instead.
"Correct grammer"
Heh. :P
Looking at the list of functions that it hooks into, I don't see pthread_rwlock*. Are the pthread_rwlock functions implemented using other pthread_* funcs? I haven't run into any problems yet with the project I'm working on, but it would be nice to run through this and make sure everything's working as expected.
<xml><I><am><so><damn>Web 2.0</damn></so></am></I></xml>
Take a deep "breathe"?
...what's so original about this issue, that deserves to be posted to Slashdot? There isn't even a FA! If the guy wanted to publicize his work (he is a developer for the linked SF project), he could at least have written an article with a concrete problem (even if the problem was made up in order to show the solution), not just some generic rant about how tricky multithreaded "programmation" is.
You can probably get a better idea of the purpose of the program on their home page (click the link in the menu of the sourceforge page that says, "Home Page").
Not sure why they didn't link directly to it?
Java's "builtin thread saftey" is simply a poor hack. The idea is to give _every_ structure a mutex. Any access to the structure requires a mutex lock.
First off, that in itself will not prevent deadlock. Secondly, it's damned inefficient.
Look: there's just no way around it. If you want to do effective (i.e. low bug, high performance) multithreaded programming, you simply have to understand what you're doing. Ultimately, the tools of your trade will be mutexes, condition variables, semaphores, etc -- the O/S primitives. Don't rely on your programming language to "automatically" use these for you, blasting out mutexes machinegun-style. Instead, figure out the logic of your program. You probably need only a small number of mutexes.
A key to effective multithreaded programming is to adhere rigidly to certain programming practices. It must _NEVER_ be the case that 2 threads have write access to a given item at the same time. Duh. But you can use fancy programming tricks to, in effect, automatically add run-time assertions to your code which assure that this practice is being adhered to. In production mode, you remove these runtime assertions.
Another good practice is, if you really need to have multiple mutexes, to arrange them into a hierarchy. When a top-level mutex is locked, no other mutex can be locked. When a second-level mutex is locked, only top-level mutexes can be locked. Etc. This hierarchy can be verified at runtime, in debug mode. Adhering to this regime will go a long way to removing the possibility for deadlocks.
Bottom line: you really have to know what you're doing in order to write good multi-threaded code. You should take the time to really study that problem space. An excellent book I've found for this purpose is "Concurrent Programming in ML". (I know -- nobody uses ML. So what? Learn the language just for the purpose of understanding the book. Then, you can apply your knowledge to any domain you're working in).
Aha, so I can only do multithreaded programming on GNU/Linux with NPTL'ed glibc or what? Other programming langunguages than C/C++ don't exist or don't do threading. What about other operating systems? Specific solutions to general problems only apply to specific manifestations of the general problem and are therefore useless for most of us.
The only good general advice about learning how to develop software on distributed systems I can give is: Read some of Andrew S Tanenbaum's books about operating systems and distributed systems in particular. The books contain knowledge you'll be able to apply to almost every system you develop software for.
Developing multithreaded is infact difficult, and any tool claiming to make it easier is worth looking at. If it works, these guys have done us all a favor. If it doesn't, at least they've made an attempt, and it may inspire others to do improve on it. Better tools are always welcome.
Generally, bash is superior to python in those environments where python is not installed.
No screenshots???!!!!
I have found there are just two ways to go.
It all comes down to livin' fast or dyin' slow. -REK, Jr.
Don't know how to multi-thread, can't spell: these fellows practically ooze competence. Perhaps the title should just be "soon-to-be ex-comp-sci majors vent"
Moshe Zadka said it better than I ever could.
If you reply, do so only to what I explicitly wrote. If I didn't write it, don't assume or infer it.
I got several concurrent threads that write the same variable!
Here's your problem. Shared state/variables is the anathema of good concurrent programming.
Here's a good place to start if you want to learn a better way...
http://www2.info.ucl.ac.be/people/PVR/bookcc.html
"It is better to die on one's feet than to live on one's knees." - Albert Camus
http://valgrind.org/ used to include a tool called hellgrind for finding just such problems. unfortunately, hellgrind has gone away for a bit (it broke when the VM was re-done to support non-x86 platforms), but julian & co are working hard to get it working again Real Soon Now (tm). if you're using x86, you can use an old release of valgrind (2.2.0 i think) and you should be fine.
personally, i can't say enough good things about valgrind. there are a couple non-obvious issues (support for sse/sse2/sse3 is still in the works, so if you get an inexplicable SIGILL, this is probably the problem), but it's saved me hundreds of hours over the past year (and i'm sure it'll save me even more in the future).
that all said, my (admittedly limited) experience with threading is that it's best to design the deadlocks away before you even touch the editor. i wonder if there are any design tools which support deadlock / contention checking at the model or design level?
Man, I wish I still had mod points to give you.
Ada was considered a BIG language when it came out. Big as in complexity. The C++ folks used to make fun of it. That's before they added templates to C++, which makes Ada look simple by comparison. Ada also came with a large set of libraries. But nowhere near what Java now comes with standard.
Anyway, when I first started learning Java, I thought that it was almost more similar to Ada in spirit than C or C++. The set of libraries seemed similar, as well as the multi-threading capabilities. To me, the biggest difference was C versus Pascal style syntax. I guess the other big difference is the JVM.
I haven't used Ada or Java enough recently to know too much about their relative merits. But it seems to me that Java doesn't give us a whole lot more than Ada had about 10 years earlier.
Software sucks. Open Source sucks less.
No thanks, I think I'll go loose my mind instead.
;)
Keep your mind to yourself, please.
Arguing about vi versus Emacs is like arguing whether it's better to make fire by rubbing sticks or banging rocks.
Single statements in Java are not atomic. You should use locks even on single statement operations. There is no guarantee that you won't get interference, otherwise.
you forgot "loose my mind"...
I always use a monitor class with multithreaded programming; the class handles all the synching and nothing is shared between the threads. In essence, the monitor is a singleton and each thread makes a call to something like g_monitor.getwork(&work) and inside getwork() I do (in pseudocode) wait on mutex, get mutex, pop work off queue, release mutex, hand work back to caller.
Googling doesn't get me a good link to post, but I know it's a common concept in multithreaded programming and has always worked for me...I have never encountered a race condition or a deadlock and I've used this on some pretty intense server apps.
Just my $0.02.
Well, yeah, you could use Java I suppose. But it's still using a shared-state concurrency model, which is inherently complex no matter what you do. Instead of that, you could use Erlang, a functional language using message-passing concurrency, and make all that complexity go away. Ericsson uses it to run telephone switches. Someone else wrote a first-person shooter with it. It can handle thousands of lightweight threads at once, and does distributed apps transparently too. For an intro to Erlang from a Java guy's perspective, check out this article by Bruce Tate.
Don't you just hate it when people reply to your signature?
that all said, my (admittedly limited) experience with threading is that it's best to design the deadlocks away before you even touch the editor.
That's not "limited" experience. That's common sense. Trying to find deadlocks, race conditions, and accidental serialization in an application by experimentally compiling and running is like trying to build a house by nailing the boards together only after they've collapsed on you.
Seriously, threads cannot be bolted on as an afterthought. You have to consciously design threads in in the first place, and if you ignore this advise and attempt to retrofit your code, then you must audit the code fully first to see where execution can be grouped into the smallest safe units for locking.
I've seen what happens when you ad hoc threading in large library that was never designed to be reentrant. Worse, I've seen what happens when you get it working on Windows but fail to realize that the UNIX version never was successfully locking the library for 3-4 years before a customer tried to build an aggressively multithreaded app on top of it and only then discover all the deadlocks that lay hidden inside.
If it's for-profit but free, you're not the customer -- you're the product (e.g., the Slashdot Beta's "audience").
It's all about OCCAM!
M0571y H@rml355.
Oh man - blast from the past, big time! We used Transputers at GEC Alsthom for ages. Occam2 was actually my first industrial-level language, rather than C (which I learnt at uni but then didn't use for a couple of years). Eventually Inmos tanked, the supply dried up (GEC bought practically all the remaining stock to keep them in spares) and we had to start again with a new platform and RTOS.
Shame really. Transputers were a great bit of kit, just underfunded, over-expensive and too far ahead of their time.
Grab.
Slashdot reported the summary line thusly:
Developers: Multi-threaded Programming Makes You Crazy? 79 of 78 comments
What's wrong with this picture?
Firstly, I apologize for my English (I'm doing my best).
I perfectly agree with some of you: this article is a slashvertisment! The main reason for that is that I previously tried to submit something more descriptive, but it was rejected. That's why I tried again with a slightly different style.
This tool (PTT) inserts trace points into the NPTL to help you to analyze multithreaded applications behaviour. He's not designed for beginners, but for people facing complex multithreaded issues. I also agree with some of you: you can use Java or some others high level languages for programming. But some applications require performance and have to be written in C. That's why PTT can be useful for some developers.
PTT has been presented at the Ottawa Linux Symposium last summer. You can find the paper here (NPTL Stabilization Project, page 111).Regards...
A debugger won't help you if you're trying to ensure correctness at too low a level of abstraction. Most languages give you a couple of low-level building blocks equivalent to the pthreads basics: threads, mutexes, and condition variables. These should not be used directly except in the simplest of circumstances. Rather, they should be used to create a set of higher-level tools that can be applied in a simple and straightforward way to your task.
The most popular way is to create a workflow model with task queues, worker threads, and job dependencies, plus a few application-specific rules to ensure that resource limitations don't cause deadlock.
The high-level model can typically be lifted right out of your proof of deadlock avoidance. Don't have one of those? It's a good idea. The proof gives you a minimal solution and confidence to implement it. Without the proof, you're going to overkill the problem out of nervousness, and you still might miss something crucial.
Read this book: Concurrent Programming in Java: Design Principles and Patterns by Doug Lea. It's a bit dry, but well worth the read. Even if you're not programming in Java, the concepts, problems, and solutions are similar. Java just offers some abstraction.
Understand the primitives, wait(), notify(), synchronized. Then learn Java 1.5's java.util.concurrent package, so you won't have to use wait/notify yourself in most cases.
Most programmers don't understand the issues that come up with concurrent programming, and if you can learn them, it's yet another way to set yourself apart. They used to teach this in operating systems class, but these days, who knows.
it seems to me that Java doesn't give us a whole lot more than Ada had
How about a runtime you can use on a cell phone?
Just junk food for thought...
It's not difficult to crosscompile an Ada compiler for embedded chips like ARMs and Xscale. This generally lacks certain featuers (ie, real time and threading)... but this is pretty trivial to get most of GNAT up and running on a major processor.
The real problem comes in when there's some cutsom hardware with a co-DSP or something like that.. since these compilers are generally customized heavily for a specific language (ie, C).
Java's implementation of Monitor does not have wait conditions -- there is only a generic wait() and a generic notify(), and then you have to set and test variables inside synchronized blocks to figure out what you are notifying for and waiting on.
notify() only wakes up a random thread, so if you have multiple threads waiting, each waiting thread should notify() upon coming out of a wait() so that other threads get served, or you need to use notifyAll(). notifyAll() is like I made an unclear question on an exam, and I have to announce during the exam a change or correction, and 100 heads bob up at once in response.
You see, I can't direct a notify() to a particular wait() -- I have to notify() one at random or notifyAll() and have each wakened thread check condition variables to see if they were the target of the notify. I am not even sure if notify() works correctly in an app with only one wait() in it because there may be other threads in the GUI or wherever that are doing a wait().
And those libraries don't help because they are built on top of notify(), notifyAll() and wait().
Your mom is a mutex.
Thus spake the Assembler programmers of the first C compilers . . .
. . . and the C programmers of the C++ compilers . .
. . . and the C++ programmers of Java . . .
This is all just my personal opinion.
The late Ole Johan Dahl's very simple and "almost mathematical" method to demonstrate (almost prove) you've got no deadlocks in your system follows below. Here is an even more simplified version on how to find all your potential deadlocks in one hour using pen and paper.
Key to the understanding is that the only way deadlocks occur is when threads try to aquire locks in the opposite order: One thread holds lock A and tries to aquire lock B, while another thread is holding lock B and waits to aquire lock A. If both threads had tried to aquire lock A before B, deadlock would be impossible. Moving second thread's locking so it too aquires lock A before before B will solve the deadlock. Aquiring the lock earlier will perhaps slow down the program, but not as much as a deadlock.
Anyway, here's the "proof" method. It is based on having to rounds of desciption and "analysis", both amazingly simple.
First round description phase:
Name all your locks and mutextes A, B, C etc. For each execution thread in you system, write down on a strip of paper a capital 'A' each time the thread aquires lock A, 'B' for lock B etc, and lowercase letters 'a', 'b' when the locks are released. Use normal regular expression syntax (parantheses, ?, +, *) to express how ifs and loops in the code affect the sequence of locking and releases. Now you have an regular text expression for each thread describing the set of locks it holds. It can help to draw a line from each 'A' to the matching 'a' etc.
Analysis phase:
Move each strip of paper relative to each other and see if you can find a situation where two threads mutually tries to aquire locks that the other thread is holding. These are your deadlock situations. Now you must inspect your code to see if the thread "overlapping" (one thread being here while the other thred executing there) actually can occur, or if there are undescribed issues like waiting for I/O that cause this to be impossible. (Waiting for I/O can be considered a 'lock' that is held by the user and temporarily 'released' as he types something, but modelling it that way is not always beneficial).
Second round: try to do the same for again, swapping roles for locks and threads. Label each thread A, B, C etc. For eack lock, on a strip of paper write the capital letter A each time thread A can aquire it and a lowercase when it is released. Since the execution interleaving of threads is rather random this can be quite messy, but if you have done the first round properly, you should be able to find the remaining deadlocks during the description phase of this round.
Removing the deadlocks:
Make sure all threads aquire locks in the same order. Hold a lock a little longer than necessary may slow your system dowsn by up to one percent, but deadlocking will slow your system down, eh, many more percents.
Jesus christ, how did this make frist pots? Are you SERIOUSLY going to waste our time explaining this? Like there is a language left on the face of the planet without multi-threaded support. And umm - yeah, locking is locking. Thanks for that. By the way, locks should be brought upwards and managed intelligently, or avoided with smart procedures and data structures, not pushed deep into the code where it can be called from many places very often. And don't post first on multithreading ever again.
It's OK Bender, there's no such thing as 2.
Take a look at java.util.concurrent (as another response has suggested). Specifically, look at:
2 SE/concurrency/index.html
e ncy/overview.html
y /j-csp1.html
;-).
"Concurrent Programming with J2SE 5.0"
http://java.sun.com/developer/technicalArticles/J
and
"Concurrency Utilities Overview"
http://java.sun.com/j2se/1.5.0/docs/guide/concurr
There's much more to concurrency in Java (especially Java 5) than simple target-object synchronization. (For example, you can set up queues to pass safely from one thread to another.)
You might also want to look at some other frameworks that have been created in Java, such as CSP, covered in a series of DeveloperWorks articles beginning with:
"CSP for Java programmers, Part 1"
http://www-128.ibm.com/developerworks/java/librar
There's a lot out there to let you think in much higher-level concepts than simple locks (although you can do that to, for the learning exercise
The reason Java runs on a cell phone is not that Java has a small footprint. It's because the computers inside cell phones are a lot beefier than they used to be. I used Ada on a 32-bit VAX system that was running at less than 100 MHz and had (I think) 32 MB of RAM. Today's cell phones are around that same magnitude of processing power.
Software sucks. Open Source sucks less.
What , you mean like a Java compiler and JVM for example?
Or did you think they were written in Java (which in turn
was written in java etc, turtles all the way down)?
Thanks for the input, I learned a lot from the discussion. I was under the impression that wait() was this big party-line affair where you couldn't notify a particular wait. Since notify() and wait() are object-monitor specific, that solves the problem I thought I had.
But in online discussions of notifyAll(), there is a lot of talk of how notifyAll() is so very necessary yet inefficient when you have lots of threads -- I haven't seen any discussion of using a separate synchronized object for every wait that you want to do, allowing use of straight notify().
I18N == Intergalacticization
I too remember Transputers and they were way too expensive and didn't keep up with performance. Eventually I found that serial processing on a 25 MHZ 386 could take the load of around 5-8 of them. After that a bit of funky coding using networked files blew them away for a fraction of the cost.
if you can so so then its a good idea to develop on a dual CPU box. That way you will catch bugs much much earlier.
note: i'm known as plugwash most places but i screwd up registering that here somehow in the past and now can't register