Skip to content

Mention Base.Lockable in "multi-threading.md" #58107

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 29 additions & 1 deletion doc/src/manual/multi-threading.md
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,9 @@ bad_read2(a) # it is NOT safe to access `a` here
```

### [Using locks to avoid data-races](@id man-using-locks)
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.
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. 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).

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:

Expand Down Expand Up @@ -337,6 +339,32 @@ julia> begin
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
to a global or closure variable) accessed by other threads. Failing to do this could have unforeseen and serious consequences.

#### [Using Base.Lockable to associate a lock and a value](@id man-lockable)
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.


```julia-repl
julia> my_array = []; # Simple empty array
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some comments about this code block:

  • I think it is overcommenting a bit. There is not really a need to comment on creating an empty array.
  • The comments become quite long on a single line, which will require horizontal scrolling to read on the website.

I'd take some inspiration of how the section below is written, ideally the text before the code should be enough to understand the code.


julia> my_locked_array = Base.Lockable(my_array); # The lock type defaults toReentrantLock(), which is fine in most cases

julia> lock(identity, my_locked_array) # The first argument is a function that is applied to the "unlocked" array, so `identity` is good for inspecting the associated value in a thread-safe manner
Any[]

julia> lock(my_locked_array) do x # The functional version of lock` along with the do-syntax` is a convenient way to work with a Lockable object
push!(x, 1)
end;

julia> lock(identity, my_locked_array) # The array is now mutated, without any risk of data-races
1-element Vector{Any}:
1

julia> my_array # The original array is identical to the one contained in my_locked_array``
1-element Vector{Any}:
1

```

### [Atomic Operations](@id man-atomic-operations)

Julia supports accessing and modifying values *atomically*, that is, in a thread-safe way to avoid
Expand Down