How to interrupt async tasks?

Oops I found the solution. It is because julia exists immediately after receiving a SIGINT by default when running a script. We can disable that by Base.exit_on_sigint(false):

running = true
function task()
    try
        i = 0
        while running
            println(i+=1)
            sleep(1)
        end
        println("Task is stopped")
    catch err
        global running = false
        @error "Task is interrupted" exception = (err, catch_backtrace())
    end
end

Base.exit_on_sigint(false)
@sync begin
    @async task()
    @async task()
end
println("Program exited")

Now the code will exit gracefully after ctrl+c:

 ~/julia  ./julia async-interupt.jl
1
1
^C┌ Error: Task is interrupted
│   exception =
│    InterruptException:
│    Stacktrace:
│     [1] poptask(W::Base.InvasiveLinkedListSynchronized{Task})
│       @ Base ./task.jl:935
│     [2] wait()
│       @ Base ./task.jl:944
│     [3] wait(c::Base.GenericCondition{Base.Threads.SpinLock})
│       @ Base ./condition.jl:124
│     [4] _trywait(t::Timer)
│       @ Base ./asyncevent.jl:129
│     [5] wait
│       @ ./asyncevent.jl:147 [inlined]
│     [6] sleep(sec::Int64)
│       @ Base ./asyncevent.jl:232
│     [7] task()
│       @ Main ~/julia/async-interupt.jl:7
│     [8] (::var"#2#4")()
│       @ Main ./task.jl:490
└ @ Main ~/julia/async-interupt.jl:11
Task is stopped
Program exited