# Locking in multithreading

**URL:** <https://discourse.julialang.org/t/locking-in-multithreading/60023>\
**Category:** Performance\
**Tags:** multithreading\
**Created:** [April 26, 2021, 9:36am UTC](https://discourse.julialang.org/t/locking-in-multithreading/60023 "2021-04-26T09:36:55Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![Philippe\_Maincon1](https://avatars.discourse-cdn.com/v4/letter/p/ec9cab/32.png) [@Philippe\_Maincon1](https://discourse.julialang.org/u/Philippe_Maincon1)\
**Post date:** [April 26, 2021, 9:36am UTC](https://discourse.julialang.org/t/locking-in-multithreading/60023/1 "2021-04-26T09:36:55Z")

</div>

Hi,

Here is a piece of code (not a MWE…)

```julia
function parallelkernel!(elca,idof,R,...) # fast typestable kernel
    @threads for iel ∈ eachindex(elca)
        r = incremental(elca[iel],...)
        #lock(lk) do
        R[idof[iel]] .+= r # potential BUG race condition
        #end
    end
end

```

I think the code is quite uncomplicated: within a threaded loop I compute vectors `r`, which I add into larger vector `R`.  
Several threads can thus be accessing the same elements in `R`, and in my understanding this is the classical “race condition” which I need to avoid. I have run this code hundreds of times, and always got the correct result, though (famous last words).

I believe I cannot use `Atomic` here, because `r` is not a primitive type (right?) but Julia’s manual mentions `lock`. However if I uncomment the `do` bracket in the above code I get `LoadError: TaskFailedException`.

Possibly, I have a subtle bug in my code, not visible in the above simplified code. In that case, I am on my own. What I would like to know is, did I completely misunderstand how `lock` is supposed to be used? The example of usage in the manual does not show the context of a threaded loop, was I wrong in assuming this usage to be the pattern?

---

<div class="post-metadata">

**Author:** ![pbayer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pbayer/32/11675_2.png) [@pbayer](https://discourse.julialang.org/u/pbayer)\
**Post date:** [April 26, 2021, 11:13am UTC](https://discourse.julialang.org/t/locking-in-multithreading/60023/2 "2021-04-26T11:13:44Z")

</div>

In your piece of code if you uncomment `#lock(lk) do` and `#end`, you will get a

```julia
ERROR: TaskFailedException
....

    nested task error: UndefVarError: lk not defined

```

you need to define `lk = ReentrantLock()` outside of the loop. An MWE:

```julia
using .Threads

function sum_race(n)
    arr = Int[]
    @threads for i in 1:n
        push!(arr, i)
    end
    sum(arr)
end

function sum_lock(n)
    arr = Int[]
    lk = ReentrantLock()
    @threads for i in 1:n
        lock(lk) do 
            push!(arr, i)
        end
    end
    sum(arr)
end

```

The first gives you a race, the second doesn’t:

```julia
julia> sum_race(1000)
ERROR: TaskFailedException
....
    nested task error: BoundsError: attempt to access 295-element Vector{Int64} at index [238]
....

julia> sum_lock(1000)
500500

```

Maybe you defined `lk` outside your function. Then it maybe something else.

---

<div class="post-metadata">

**Author:** ![Philippe\_Maincon1](https://avatars.discourse-cdn.com/v4/letter/p/ec9cab/32.png) [@Philippe\_Maincon1](https://discourse.julialang.org/u/Philippe_Maincon1)\
**Post date:** [April 26, 2021, 11:40am UTC](https://discourse.julialang.org/t/locking-in-multithreading/60023/3 "2021-04-26T11:40:57Z")

</div>

Hi Paul

I just missed the part about defining lk in the manual, simple as that!

This solves the issue, thank you!

---

<div class="post-metadata">

**Author:** ![tkf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tkf/32/17635_2.png) [@tkf](https://discourse.julialang.org/u/tkf)\
**Post date:** [April 26, 2021, 9:02pm UTC](https://discourse.julialang.org/t/locking-in-multithreading/60023/4 "2021-04-26T21:02:22Z")

</div>

FYI, reduction using a lock is often a bad idea. With lock, `R[idof[iel]] .+= r` is _not_ run in parallel. For the code in the OP to be parallelized well, the time takes for `R[idof[iel]] .+= r` has to be very small relative to `incremental(elca[iel],...)`. If you can keep `nthreads` copies of `R` in memory, it may make sense to use solution without the lock. For more information, see:

- [A quick introduction to data parallelism in Julia](https://juliafolds.github.io/data-parallelism/tutorials/quick-introduction/#manual_reductions)
- [How to do X in parallel? · FLoops](https://juliafolds.github.io/FLoops.jl/dev/howto/parallel/)

---

<div class="post-metadata">

**Author:** ![Philippe\_Maincon1](https://avatars.discourse-cdn.com/v4/letter/p/ec9cab/32.png) [@Philippe\_Maincon1](https://discourse.julialang.org/u/Philippe_Maincon1)\
**Post date:** [April 27, 2021, 6:00am UTC](https://discourse.julialang.org/t/locking-in-multithreading/60023/5 "2021-04-27T06:00:00Z")

</div>

Hi Takafumi,

> [@tkf](#):
>
> With lock, `R[idof[iel]] .+= r` is _not_ run in parallel.

Yes, that makes sense. Most CPU time is spent on `incremental`, so it’s not a disaster, but indeed I can see that more time is spent on this addition than when taking risks without a lock. I actually tried exactly what you suggest. I somehow botched it and got bad performance, but I’ll have another go.

And thank you for the links, I will study them!
