[Restructure a bit. Bryan O'Sullivan **20080528051749] { hunk ./en/ch25-concurrent.xml 474 - programming in other languages apply to Haskell. + programming in other languages apply equally to Haskell. hunk ./en/ch25-concurrent.xml 476 - The first of these is deadlock, in - which two or more threads get stuck forever due to a clash over - access to shared resources. One classic way to make a - multithreaded program deadlock is to forget the order in which - we must acquire locks. This kind of bug is so common, it has a - name: lock order inversion. While Haskell - doesn't provide locks, the MVar type is prone to - the order inversion problem. Here's a simple example. + + Deadlock + A common problem with concurrent code is + deadlock, in which two or more threads + get stuck forever due to a clash over access to shared + resources. One classic way to make a multithreaded program + deadlock is to forget the order in which we must acquire + locks. This kind of bug is so common, it has a name: + lock order inversion. While Haskell + doesn't provide locks, the MVar type is prone to + the order inversion problem. Here's a simple example. hunk ./en/ch25-concurrent.xml 490 - If we run this in &ghci;, it will usually&emdash;but not - always&emdash;print nothing, indicating that both threads have - gotten stuck. + If we run this in &ghci;, it will usually&emdash;but not + always&emdash;print nothing, indicating that both threads have + gotten stuck. hunk ./en/ch25-concurrent.xml 494 - The problem with the nestedModification - function is easy to spot. In the first thread, we take the - MVar a, then - b. In the second, we take - b, then a. If the first - thread succeeds in taking a and the second - takes b, both threads will block: each tries - to take an MVar that the other has already emptied, - so neither can make progress. + The problem with the + nestedModification function is easy to + spot. In the first thread, we take the MVar + a, then b. In the + second, we take b, then + a. If the first thread succeeds in taking + a and the second takes + b, both threads will block: each tries to + take an MVar that the other has already emptied, + so neither can make progress. hunk ./en/ch25-concurrent.xml 505 - In real code, these kinds of inversion problems are much - more difficult to spot. The taking of MVars is - often spread across several functions in different files, making - visual inspection more tricky. Worse, these problems are often - intermittent, which makes them tricky to - even reproduce, never mind isolate and fix. + In real code, these kinds of inversion problems are much + more difficult to spot. The taking of MVars is + often spread across several functions in different files, + making visual inspection more tricky. Worse, these problems + are often intermittent, which makes them + tricky to even reproduce, never mind isolate and fix. + hunk ./en/ch25-concurrent.xml 513 - Concurrent software is also prone to - starvation, in which one thread - hogs a shared resource, preventing another from - using it. It's easy to imagine how this might occur: one thread - calls modifyMVar with a body that executes - for 100 milliseconds, while another calls - modifyMVar on the same MVar - with a body that executes for 1 millisecond. The second thread - cannot make progress until the first puts a value back into the - MVar. + + Starvation hunk ./en/ch25-concurrent.xml 516 - The non-strict nature of the MVar type can - either exacerbate or cause a starvation problem. If we put a - thunk into an MVar that will be expensive to - evaluate, and take it out of the MVar in a thread - that otherwise looks like it ought to be - cheap, that thread could suddenly become computationally - expensive if it has to evaluate the thunk. This makes the - advice we gave in - particularly relevant. + Concurrent software is also prone to + starvation, in which one thread + hogs a shared resource, preventing another from + using it. It's easy to imagine how this might occur: one + thread calls modifyMVar with a body that + executes for 100 milliseconds, while another calls + modifyMVar on the same MVar + with a body that executes for 1 millisecond. The second + thread cannot make progress until the first puts a value back + into the MVar. hunk ./en/ch25-concurrent.xml 527 - Fortunately, the APIs for concurrency that we have covered - here are by no means the end of the story. A more recent - addition to Haskell, Software Transactional Memory, is both - easier and safer to work with. We will discuss it in chapter - XXX. + The non-strict nature of the MVar type can + either exacerbate or cause a starvation problem. If we put a + thunk into an MVar that will be expensive to + evaluate, and take it out of the MVar in a thread + that otherwise looks like it ought to be + cheap, that thread could suddenly become computationally + expensive if it has to evaluate the thunk. This makes the + advice we gave in + particularly relevant. + + + + Is there any hope? + + Fortunately, the APIs for concurrency that we have covered + here are by no means the end of the story. A more recent + addition to Haskell, Software Transactional Memory, is both + easier and safer to work with. We will discuss it in chapter + XXX. + }