I’m trying to do something that feels like should be easy but I can’t figure it out.
Basically, let’s say I have a function (doer) which takes a while to run and I want to have another function printer printing stuff to stdout while doer is running - without messing with doer itself.
The motivation for this is that in Term.jl I need to have a function rendering progress bars information while user’s code runs. I can’t edit the user’s code and I want the progress bar rendering to happen continuously in the background.
This is a MWE of what I have so far:
mutable struct Status
on::Bool
end
function printer(s::Status)
println("Printer turning on")
while s.on
println("PRINTING")
sleep(0.01)
end
println("Printer turning off")
end
function doer()
# println("Doer getting started")
s = Status(true)
# @async printer(s)
Base.Threads.@spawn printer(s)
for k in 1:50000
x = rand(100, 100)
y = x.^2 .- rand(100, 100)
end
s.on = false
nothing
end
print("\n\n")
doer()
My expectation is that printer should be running in the background while doer finishes its stuff, but this is what the output lokos like:
Printer turning on
Printer turning off
I’ve tried using @async and Task and @spawn… same result. The only thing that makes it work is adding a sleep call in the loop in doer.
I first tried accumulating from the loop so it couldn’t be removed by dead code elimination. That didn’t seem to make a difference. Then I remembered that GC pauses all threads, and I noticed the code was very allocating; I made the operations in the loop non-allocating, and suddenly got tons of printing.
using Random
function doer()
# println("Doer getting started")
s = Status(true)
# @async printer(s)
Base.Threads.@spawn printer(s)
accum = 0.0
x = zeros(100, 100)
y = similar(x)
for k in 1:50000
rand!(x)
@. y = x^2 - rand()
accum += sum(y)
end
s.on = false
return accum
end
edit: oops! This was just because I forgot to import Random; I lost the error in the sea of printing which never gets turned off… when I fix it, I don’t see the printing.
Thanks everyon for looking into this, I really appreciate the help.
@skleinbo 'and @chengchingwen had it right: wrapping doer or its content in another @spaw makes it work like a charm, I’ve also added a wait on the task generated by @spawn doer() and it does exactly what I need. Progress bar in Term.jl won’t be hanging any more!
@ericphanson thanks for the help, unfortunately I can’t ensure that users’ code (which would be in doer) is not allocating!