Atell
Atell

Reputation: 23

Why doesn't code in `@async` run as expected with `println`?

When I run the following code, it runs as expected (that is, it outputs both "endless loop" and "loop in 100"):

@async while true
print("endless loop\n")
end
for i in 1:100
print("loop in 100\n")
end

However, for some reason, when I changed print to println, the result was very different

@async while true
println("endless loop")
end
for i in 1:100
println("loop in 100")
end

This code only outputs "loop in 100" a hundred times. Any ideas why?

Actually, println seems to always impact multithread execution:

## works as expected, prints "loop in 100" 100 times and "endless loop" 3 times in between
@async while true
print("endless loop\n")
end
@sync for i in 1:100
@async print("loop in 100\n")
end
## simply switching to println makes a program that outputs "endless loop" forever
@async while true
println("endless loop")
end
@sync for i in 1:100
@async println("loop in 100")
end

I used println for debug output but then realised it's unusable for that purposes. Any suggestion why exactly this works the way it works would be very helpful. I'm executing it with Julia 1.6.1 so that I can use automatic thread detection julia -t auto file.jl.

Upvotes: 2

Views: 338

Answers (2)

attdona
attdona

Reputation: 18943

The println implementation for julia 1.6.1 includes nested lock calls:

println(io::IO, xs...) = print(io, xs..., "\n")

function print(io::IO, xs...)
    lock(io)
    try
        for x in xs
            print(io, x)
        end
    finally
        unlock(io)
    end
    return nothing
end

function print(io::IO, x)
    lock(io)
    try
        show(io, x)
    finally
        unlock(io)
    end
    return nothing
end

In your simplified example this implementation has a side effect on the order of task scheduling.

Compare test1.jl using println:

t = @async for i in 1:3
    println("endless loop")
end

for i in 1:3
    println("loop in 100")
end

wait(t)

$ julia test1.jl 
loop in 100
loop in 100
loop in 100
endless loop
endless loop
endless loop

With test2.jl using print:

t = @async for i in 1:3
    print("endless loop\n")
end

for i in 1:3
    print("loop in 100\n")
end

wait(t)


$ julia test2.jl 
loop in 100
endless loop
loop in 100
endless loop
loop in 100
endless loop

Note that the async task should be awaited otherwise the process exits without giving a chance to run the async task.

But the "perturbing" effect of println on task scheduling disappear when the tasks block and yield controls to the scheduler, as in real world scenario, simulated intest3.jl with a sleep:

t = @async for i in 1:3
    println("endless loop")
    sleep(1)
end

for i in 1:3
    println("loop in 100")
    sleep(1)
end

wait(t)


$ julia test3.jl 
    loop in 100
    endless loop
    loop in 100
    endless loop
    loop in 100
    endless loop

Upvotes: 1

Bill
Bill

Reputation: 6086

Clearly println() behaves differently from print, and likes to finish before it allows other tasks printing. You could write the \n into the print() yourself though:

@async while true
    print("endless loop\n")
end
for i in 1:100
    print("loop in 100\n")
end

if you really need the interleaving between threaded prints.

Upvotes: 1

Related Questions