Synchronizing 2 loops in multi-threading

What’s the best way to synchronize 2 while loops, each running on its own thread?

In the example below, loop1 runs 80 times per second while loop2 (blocking/IO) runs 20 times per second. Currently, without any locks/synchronizations, the timing of the loops diverges away as time passes.

timer1 = Timer(0, interval=1/80)
timer2 = Timer(0, interval=1/20)

q_loop = Threads.Atomic{Bool}(true)

function loop1()
    wait(timer1)
    while q_loop.value
        # do task...
        wait(timer1)
    end
end

function loop2()
    wait(timer2)
    while q_loop.value
        # do a different task involving IO (blocking)
        wait(timer2)
    end
end

function loop()
    @sync begin
        Threads.@spawn loop1()
        Threads.@spawn loop2()
    end
end

# start and run the loops until the user requests to stop
q_loop.value = true
@async loop()

# stop the loops
q_loop.value = false

If the loops perform independent operations, then why do they need to be synchronized? If the are interdependent, synchonize by communicating (via a Channel).

1 Like

I’m not sure what all your constraints are but I feel like the easiest way to keep the two functions in lock step are to run them off of one timer like:

q_loop = Threads.Atomic{Bool}(true)

function loop1(ch1)
    for _ in ch1
        println("Quick")
    end
end

function loop2(ch2)
    for _ in ch2
        println("Slow")
    end
end

function loop()
    ch1 = Channel{Bool}(16)
    ch2 = Channel{Bool}(16)
    q_loop[] = true

    @async begin
        sleep(2)
        q_loop[] = false
    end

    @sync begin
        @Threads.spawn loop1(ch1)
        @Threads.spawn loop2(ch2)

        local c = 1
        Timer(0, interval=1/80) do t
            if q_loop[] == false
                close(ch1)
                close(ch2)
                close(t)
            elseif c == 4
                put!(ch1, true)
                put!(ch2, true)
                c = 1
            else
                put!(ch1, true)
                c += 1
            end
        end
    end

end

Here you have 1 timer that runs at 1/80 which wakes up loop1() every tick and loop2() every 4th tick. The channels of size 16 give you a little bit of slop if loop1() takes over 1/80 a second to perform it’s operation or loop2() takes over 1/20 to do it’s thing. However if either loops takes longer consistently you are going to have a general slow down.

You could resolve this by checking the channels before putting true into them, if they are full generate some sort of alert. You could also replace the Channels with Events in which case if either loop takes to long it will just “miss” the next tick.