Reputation: 45
I have a Julia code which runs for quite sometime. I want to run the code for 3 hours and then terminate it. Does anyone know what is the most efficient way to do this. Would appreciate any advice. Thanks
Upvotes: 1
Views: 1350
Reputation: 582
Here's a solution using threads. I'm just begginging coding in Julia so it's not high quality code, but it works. You first need to wrap your function as a Task, by using the @task macro, and then call runTask():
function runTask(origtask:: Task, timeoutms:: Int)
startTime = Dates.datetime2epochms(now())
function internal_task(taskFinished)
try
schedule(origtask)
yield()
wait(origtask)
if istaskdone(origtask)
taskFinished[] = true
Base.task_result(origtask)
else
throw(ErrorException("Task is not done even after wait() for it to finish - something is wrong"))
end
catch e
@warn "Error while processing task: $e"
taskFinished[] = true
missing
end
end
taskFinished = Threads.Atomic{Bool}(false)
thread = Threads.@spawn internal_task(taskFinished)
while !taskFinished[] && (Dates.datetime2epochms(now()) - startTime) < timeoutms
sleep(0.1)
end
if taskFinished[]
return fetch(thread)
end
# Task timeouted
origtask.exception = InterruptException()
return missing
end # function
The worst part of this code is active waiting using sleep(0.1). I guess I could get rid of it by spawning one more thread, so there would be 3 in total: one would execute the actual work from the origtask and after doing it would notify a condition, one would wait on the mentioned condition and another one would sleep for the timeout number of seconds and then notify the condition.
Upvotes: 2
Reputation: 42214
I recommend using Distributed
to spawn your function as a new process and control it's time (I believe I have been answering similar question but I cannot find the answer).
Here is the code:
using Distributed
function run_with_timeout(timeout::Int,f::Function, wid::Int)
result = RemoteChannel(()->Channel{Tuple}(1));
@spawnat wid put!(result, (f(),myid()))
res = (:timeout, wid)
time_elapsed = 0.0
while time_elapsed < timeout && !isready(result)
sleep(0.5)
time_elapsed += 0.5
end
if !isready(result)
println("Timeout! at $wid")
else
res = take!(result)
end
return res
end
You can use it like this (please note how external packages are imported):
wid = addprocs(1)[1]
@everywhere using DataFrames
@everywhere function ff()
sleep(2)
#code fir making heavy computations
#this is the place to write whatever you need
return DataFrame(x=[1,2],y=[1,3])
end
Now let us run it. Note that the second returned value is the workerid to run computations (you might want to remove it):
julia> run_with_timeout(10,() ->(try;ff();catch ee;dump(ee);end ),wid)
(2×2 DataFrame
│ Row │ x │ y │
│ │ Int64 │ Int64 │
├─────┼───────┼───────┤
│ 1 │ 1 │ 1 │
│ 2 │ 2 │ 3 │, 2)
If we give our code only 1 sec to execute it will fail:
julia> run_with_timeout(1,() ->(try;ff();catch ee;dump(ee);end ),wid)
Timeout! at 2
(:timeout, 2)
Finally, since the timeout occured the process should be killed (otherwise it would continue to execute):
rmprocs(wid)
Upvotes: 1
Reputation: 6086
@async and @sync are really useful for coroutine type process control in Julia. You can start an async process that calls exit at a later point so as to terminate the entire program:
function killafterseconds(s)
@async begin
sleep(s)
println("terminating after $s seconds")
exit(0)
end
end
function countdown(n)
for t in n:-1:0
println(t)
sleep(1)
end
end
killafterseconds(10)
countdown(10000)
Upvotes: 4