I want to call all function that I put in a dict. So far that works great.
But as soon as I add another function the task fails.
julia> d = Dict()
Dict{Any,Any} with 0 entries
julia> d[1] = (()->"1", ())
(var"#21#22"(), ())
julia> t = @async while true
for (k, (f, args)) in d
println(f(args...))
end
sleep(2)
end
Task (runnable) @0x0000000013a42850
1
1
1
julia> d[2] = (()->"2", ())
(var"#25#26"(), ())
julia> t
Task (failed) @0x0000000013a42850
MethodError: no method matching (::var"#25#26")()
The applicable method may be too new: running in world age 27216, while current world is 27217.
Closest candidates are:
#25() at none:1 (method too new to be called from this world context.)
macro expansion at .\none:5 [inlined]
(::var"#23#24")() at .\task.jl:358
My bet it’s because you are using the function as a key in a Dict, which is odd enough. Why not use a vector, or do you want to keep the same function from being added twice?
Sorry, you are correct, my bad. However this works:
d = Dict()
d[1] = (()->"1", ())
d[2] = (()->"2", ())
t = @async while true
for (k, (f, args)) in d
println(f(args...))
end
sleep(2)
end
So it isn’t an issue of calling multiple functions from a dict, it’s adding an anonymous function to the Dict after the async task is started. This works however:
function one()
"1"
end
function two()
"2"
end
d = Dict()
d[1] = (one, ())
t = @async while true
for (k, (f, args)) in d
println(f(args...))
end
sleep(2)
end
sleep(5)
d[2] = (two, ())
sleep(3)
t
So according to the error The applicable method may be too new: running in world age 27216, while current world is 27217. The async task is running when the “world age” was 27216, but you passed in a function that was declared when the “world age” was 27217 so the async task can’t find that function since it’s running in an environment that is a step behind.
Stop the task before adding something to the Dict? Maybe something like:
mutable struct BackgroundTask
todo::Dict{Int, Tuple}
stop::Ref{Bool}
task::Union{Task, Nothing}
BackgroundTask() = new(Dict{Int, Tuple}(), Ref{Bool}(false), nothing)
end
function queue(func, b::BackgroundTask, key, args)
if b.task != nothing
b.stop[] = true
wait(b.task)
end
b.stop[] = false
b.todo[key] = (func, args)
b.task = @async while b.stop[] == false
for (k, (f, args)) in b.todo
println(f(args...))
end
sleep(2)
end
end
b = BackgroundTask()
queue(b, 1, ()) do
"1"
end
sleep(5)
queue(b, 2, ()) do
"2"
end
sleep(5)
The calls to queue() could be delayed waiting for the task to finish…you could work around that by changing queue to:
function queue(func, b::BackgroundTask, key, args)
@async begin
if b.task != nothing
b.stop[] = true
wait(b.task)
end
b.stop[] = false
b.todo[key] = (func, args)
b.task = @async while b.stop[] == false
for (k, (f, args)) in b.todo
println(f(args...))
end
sleep(2)
end
end
end
So that when the background task finishes a loop, the Dict is updated and the task restarted.