You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: src/tutorial.md
+36
Original file line number
Diff line number
Diff line change
@@ -23,6 +23,7 @@ The availability of these features is comparable to built-in language features l
23
23
24
24
*[Basic threading](#basic-threading)
25
25
*[Thread synchronisation with MVars](#thread-synchronisation-with-mvars)
26
+
*[Sharing state with MVars](#sharing-state-with-mvars)
26
27
*[Channels](#channels)
27
28
*[Duplicating channels](#duplicating-channels)
28
29
@@ -139,6 +140,41 @@ Running this, you should see the following output:
139
140
140
141
[See the whole program.](./Ex2MVars.hs)
141
142
143
+
## Sharing state with MVars
144
+
145
+
In the [previous example](#thread-synchronisation-with-mvars), we used an `MVar` as a way to wait for a concurrent action's result.
146
+
Sometimes we want long-running actions to share state, and we want to make sure they don't run into race conditions when modifying that state.
147
+
`MVars` can also fill this role, though we'll see a much better way to do it when we look at STM later.
148
+
149
+
For now, we'll reproduce a classic concurrency example: we have a single counter, which multiple threads try to increment several times.
150
+
In the presence of race conditions, errors will mean often the final result isn't what you would expect, if you were to run each thread synchronously.
151
+
152
+
```haskell
153
+
main =do
154
+
counter <- newMVar 0
155
+
156
+
let increment = modifyMVar_ counter (\c ->return$! c +1)
157
+
let incrementer =do
158
+
replicateM 1000 increment
159
+
return()
160
+
161
+
threads <- replicateM 5 (forkIO incrementer)
162
+
163
+
sleepMs 10
164
+
count <- takeMVar counter
165
+
print count
166
+
```
167
+
168
+
Here, `increment` uses `modifyMVar_` to apply a function to the contents of an `MVar`, when it exists.
169
+
This operation blocks, naturally, and also blocks other threads from operating on the `MVar`'s contents at the same time.
170
+
171
+
`increment` uses `$!`, which is a _strict_ form of function application, to avoid simply storing a huge `(0 + (1 + (1 + ...` think inside `counter`.
172
+
This strict application ensures that `c + 1` is evaluated before it is stored in the `MVar`.
173
+
174
+
Note that in this case, our application is essentially single-threaded, because all threads block on `counter`.
175
+
So we gain no _time_ efficiency from using strict application (`$!`) - we could just as easily build up a huge thunk, then have the main thread evaluate it when we `print count`.
176
+
However, this costs memory, so evaluating the counter strictly at every step makes the program more _space_ efficient.
177
+
142
178
## Channels
143
179
144
180
Now that we've talked about `MVar`s, let's get up to speed with channels in Haskell.
0 commit comments