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
Mention Base.Lockable in "multi-threading.md" (#58107)
When I originally learned about multithreading and locks (and ended up
rewriting
[https://docs.julialang.org/en/v1/manual/multi-threading/#man-using-locks](Using
locks to avoid data-races)), I was puzzled to find that the association
of a lock and a value was the mental task of the programmer, and not the
programmatic task of the program. I made that explicit in that section
of the manual, writing the following:
> Note that the link between a lock and a variable is made by the
programmer, and not the program.
I was therefore very happy to see Base.Lockable introduced in Julia
1.11. What was missing, was any mention of it in the relevant section of
the manual.
This PR adds a subsection under locks (So a forth level of headings, not
sure if that is fine), showcasing and reccomending the use of
Base.Lockable. I have made heavy use of comments in the example, which
breaks with the general style, but adds valuable interpretation along
the way. I am very open restructuring that part in particular.
---------
Co-authored-by: Chengyu Han <[email protected]>
Co-authored-by: Neven Sajko <[email protected]>
Co-authored-by: Oscar Smith <[email protected]>
Copy file name to clipboardExpand all lines: doc/src/manual/multi-threading.md
+45-1Lines changed: 45 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -306,7 +306,9 @@ bad_read2(a) # it is NOT safe to access `a` here
306
306
```
307
307
308
308
### [Using locks to avoid data-races](@id man-using-locks)
309
-
An important tool to avoid data-races, and thereby write thread-safe code, is the concept of a "lock". A lock can be locked and unlocked. If a thread has locked a lock, and not unlocked it, it is said to "hold" the lock. If there is only one lock, and we write code the requires holding the lock to access some data, we can ensure that multiple threads will never access the same data simultaneously. Note that the link between a lock and a variable is made by the programmer, and not the program.
309
+
An important tool for avoiding data races, and writing thread-safe code in general, is the concept of a "lock". A lock can be locked and unlocked. If a thread has locked a lock, and not unlocked it, it is said to "hold" the lock. If there is only one lock, and we write code that requires holding the lock to access some data, we can ensure that multiple threads will never access the same data simultaneously.
310
+
311
+
Note that the link between a lock and a variable is made by the programmer, and not the program. A helper-type [`Base.Lockable`](@ref) exists that helps you associate a lock and a value. This is often more safe than keeping track yourself, and is detailed under [Using Base.Lockable to associate a lock and a value](@ref man-lockable).
310
312
311
313
For example, we can create a lock `my_lock`, and lock it while we mutate a variable `my_variable`. This is done most simply with the `@lock` macro:
312
314
@@ -342,6 +344,48 @@ julia> begin
342
344
All three options are equivalent. Note how the final version requires an explicit `try`-block to ensure that the lock is always unlocked, whereas the first two version do this internally. One should always use the lock pattern above when changing data (such as assigning
343
345
to a global or closure variable) accessed by other threads. Failing to do this could have unforeseen and serious consequences.
344
346
347
+
#### [Using Base.Lockable to associate a lock and a value](@id man-lockable)
348
+
As mentioned in the previous section, the helper-type [`Base.Lockable`](@ref) can be used to programmatically ensure the association between a lock and a value. This is generally recommended, as it is both less prone to error and more readable for others compared to having the association only by convention.
349
+
350
+
Any object can be wrapped in `Base.Lockable`:
351
+
```julia-repl
352
+
julia> my_array = [];
353
+
354
+
julia> my_locked_array = Base.Lockable(my_array);
355
+
```
356
+
357
+
If the lock is held, the underlying object can be accessed with the empty indexing notation:
358
+
```julia-repl
359
+
julia> begin
360
+
lock(my_locked_array)
361
+
try
362
+
push!(my_locked_array[], 1)
363
+
finally
364
+
unlock(my_locked_array)
365
+
end
366
+
end
367
+
1-element Vector{Any}:
368
+
1
369
+
```
370
+
371
+
It is usually easier and safer to pass a function as the first argument to `lock`. The function is applied to the unlocked object, and the locking/unlocking is handled automatically:
0 commit comments