Title here
Summary here
In the previous example, we saw how to manage simple counter state using atomic operations. For more complex state, we can use a mutex to safely access data across multiple tasks.
using Base.Threads
# Container holds a dictionary of counters; since we want to
# update it concurrently from multiple tasks, we
# add a ReentrantLock to synchronize access.
mutable struct Container
lock::ReentrantLock
counters::Dict{String, Int}
end
# Lock the mutex before accessing `counters`; unlock
# it at the end of the function.
function inc!(c::Container, name::String)
lock(c.lock) do
c.counters[name] = get(c.counters, name, 0) + 1
end
end
# Note that ReentrantLock is initialized in the constructor
function Container()
Container(ReentrantLock(), Dict{String, Int}())
end
function main()
c = Container()
c.counters["a"] = 0
c.counters["b"] = 0
# This function increments a named counter
# in a loop.
function doIncrement(name::String, n::Int)
for i in 1:n
inc!(c, name)
end
end
# Run several tasks concurrently; note
# that they all access the same Container,
# and two of them access the same counter.
@sync begin
@spawn doIncrement("a", 10000)
@spawn doIncrement("a", 10000)
@spawn doIncrement("b", 10000)
end
println(c.counters)
end
main()
Running the program shows that the counters updated as expected.
$ julia mutexes.jl
Dict("b" => 10000, "a" => 20000)
In this Julia version:
ReentrantLock
from Base.Threads
instead of sync.Mutex
.Container
struct is defined as mutable
to allow modification of its fields.inc!
function uses a do
block with lock
to ensure thread safety.@spawn
to create concurrent tasks instead of goroutines.@sync
is used to wait for all spawned tasks to complete, similar to WaitGroup
in Go.Dict
is used instead of Go’s map
.This example demonstrates how to use mutexes in Julia to safely manage state across multiple concurrent tasks.
Next, we’ll look at implementing this same state management task using only tasks and channels.