@@ -87,17 +87,87 @@ julia> ids
8787 For more information, read
8888 [ PSA: Thread-local state is no longer recommended] ( https://julialang.org/blog/2023/07/PSA-dont-use-threadid/ ) .
8989
90- ### Thread safety
90+ ### Data races
9191
92- When working with threads, you must avoid race conditions, in which two
93- threads attempt to write to the same variable at the same time. In the above
94- example we avoided a race condition by using ` ReentrantLock ` . See the
95- [ Multi-threading] ( https://docs.julialang.org/en/v1/manual/multi-threading/ )
92+ When working with threads, you must avoid data races. A data race occurs when
93+ multiple threads access the same variable at the same time, at least one thread
94+ modifies the variable, and the order of the reads and writes are not properly
95+ coordinated.
96+
97+ Here's an example of a data race:
98+ ```` julia
99+ julia> begin
100+ x = Ref (0 )
101+ Threads. @threads for i in 1 : Threads. nthreads ()
102+ for i in 1 : 1_000
103+ x[] += 1
104+ end
105+ end
106+ x[]
107+ end
108+ 1106
109+ ````
110+ The expected answer is ` 4_000 ` (because there are four threads each incrementing
111+ 1,000 times), but the actual result is much smaller. Moreover, the result is
112+ non-deterministic; if we re-ran this code, we would get a different value each
113+ time.
114+
115+ We got the wrong answer because multiple threads are reading and writing ` x `
116+ at the same time without coordination. For example, the following sequence
117+ could occur:
118+
119+ * Assume ` x[] ` currently has a value of ` 3 `
120+ * Thread A reads ` x[] ` to get ` 3 `
121+ * Thread B reads ` x[] ` to get ` 3 `
122+ * Thread A writes ` x[] = 3 + 1 = 4 `
123+ * Thread B writes ` x[] = 3 + 1 = 4 `
124+ * The final value of ` x[] ` is ` 4 `
125+
126+ The write from Thread A is overwritten, and so the value of ` x[] ` has increased
127+ by ` 1 ` instead of ` 2 ` .
128+
129+ Similar to the earlier ` ids ` example, we can fix this data race using a
130+ ` ReentrantLock ` . The lock ensures that only one thread can update (read and then
131+ write) ` x ` at a time. Now we get the correct answer:
132+ ```` julia
133+ julia> begin
134+ x = Ref (0 )
135+ l = ReentrantLock ()
136+ Threads. @threads for i in 1 : Threads. nthreads ()
137+ for i in 1 : 1_000
138+ lock (l) do
139+ x[] += 1
140+ end
141+ end
142+ end
143+ x[]
144+ end
145+ 4000
146+ ````
147+
148+ With the lock, the sequence of events goes something like:
149+
150+ * Assume ` x[] ` currently has a value of ` 3 `
151+ * Thread A acquires the lock
152+ * Thread A reads ` x[] ` to get ` 3 `
153+ * Thread B asks for the lock, but is denied because A is currently using it
154+ * Thread A writes ` x[] = 3 + 1 = 4 `
155+ * Thread A releases the lock
156+ * Thread B acquires the lock
157+ * Thread B reads ` x[] ` to get ` 4 `
158+ * Thread B writes ` x[] = 4 + 1 = 5 `
159+ * Thread B releases the lock
160+ * The final value of ` x[] ` is ` 5 `
161+
162+ See the [ Multi-threading] ( https://docs.julialang.org/en/v1/manual/multi-threading/ )
96163section of the Julia documentation for more details.
97164
98- JuMP models are not thread-safe. Code that uses multi-threading to
99- simultaneously modify or optimize a single JuMP model across threads may error,
100- crash Julia, or silently produce incorrect results.
165+ ### JuMP models are not thread-safe
166+
167+ An object is thread-safe if it can be modified by separate threads without
168+ causing a data race. JuMP models are not thread-safe. Code that uses
169+ multi-threading to simultaneously modify or optimize a single JuMP model
170+ across threads may error, crash Julia, or silently produce incorrect results.
101171
102172For example, the following incorrect use of multi-threading crashes Julia:
103173``` julia
0 commit comments