Is there a way to kill a thread spawned by Threads.@spawn macro?
Can you perhaps use Base.throwto
to throw an exception to the task?
Thank you for your reply. I will give it a go as you suggest.
Actually I needed to be more specific. I am trying to do the following:
Threads.@spawn run(pipeline(`adb shell "logcat -v time | grep onScanResult"`, io))
Initially I discovered that run()
was a blocking call and I could’t do anything after that. So I let a separate thread call run()
.
I am naively expecting that if I could somehow kill the thread, then the process launched by that thread may also be killed…but this is a wrong understanding?
I’m not sure about that, but I know that Base.throwto
works well when throwing an exception to a task started with @async
. Tasks on other threads might behave differently.
You need to get the task handle to kill it, like that:
using .Threads
function hanging()
println("I'm hanging on thread:", threadid())
while true
sleep(1)
end
end
julia> hang = Threads.@spawn hanging();
julia> I'm hanging on thread:2
julia> hang
Task (runnable) @0x000000010f5698d0
julia> schedule(hang, ErrorException("stop"), error=true)
Task (failed) @0x000000010f5698d0
stop
try_yieldto(::typeof(Base.ensure_rescheduled), ::Base.RefValue{Task}) at ./task.jl:611
....
Thank you for the detailed information! I think now I can leverage your code to put extra code to kill a process inside the thread before it’s death.
According to @jameson, schedule(..., error=true)
is not the correct way to terminate the task already started and there is no way to safely interrupt a task. Stop/terminate a (sub)task started with @async - #8 by jameson
First of all, you don’t need to use thread to run I/O concurrently. @async
is enough. Also, for running external processes concurrently, you don’t even need to use @async
:
julia> proc = run(`sleep 60`; wait = false)
Process(`sleep 60`, ProcessRunning)
julia> kill(proc)
julia> proc
Process(`sleep 60`, ProcessSignaled(15))
killing is never safe, I didn’t suggest otherwise. A safe way would be a task handling its own channel, like the following:
struct Stop end
struct Continue end
function safe_hanging(ch::Channel)
println("I'm hanging on thread ", threadid())
signal = Continue()
while true
isready(ch) && (signal = take!(ch))
signal == Stop() && break
sleep(1)
end
println("stopped!")
end
julia> mytask = Ref{Task}();
julia> ch = Channel(safe_hanging, taskref=mytask, spawn=true)
I'm hanging on thread 2
Channel{Any}(sz_max:0,sz_curr:0)
julia> mytask[]
Task (runnable) @0x000000011001f850
julia> put!(ch, Stop());
stopped!
julia> mytask[]
Task (done) @0x000000011001f850
Also a task with its own try catch
error handling could be a safe way.
Yes, you need so-called “cancellation token” for safe task cancellation. It would be nice to have a uniform API to do this in Julia. For more discussion see:
https://github.com/JuliaLang/julia/issues/33248
I also created a proof-of-concept that has an API cancel!(::TaskContext)
: GitHub - tkf/Awaits.jl: [WIP] Structured concurrency for parallel computing
@pbayer, @tkf, @baggepinnen, thank you all for your valuable instructions and concrete code examples!
It took me quite a while until I completely familiarized myself with every details in your answers.
@tkf, It took me days to catch the presence of wait=false
in run() call
. How silly of me!