# Different spawning behavior of @spawn and Channel(..., spawn=true)

**URL:** https://discourse.julialang.org/t/different-spawning-behavior-of-spawn-and-channel-spawn-true/48413
**Category:** General Usage
**Tags:** multithreading
**Created:** [October 15, 2020, 7:44am UTC](https://discourse.julialang.org/t/different-spawning-behavior-of-spawn-and-channel-spawn-true/48413 "2020-10-15T07:44:45Z")
**Posts on this page:** 5
**Page:** 1

<div class="post-metadata">

### Author: ![pbayer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pbayer/32/11675_2.png) [@pbayer](https://discourse.julialang.org/u/pbayer)
#### Post date: [October 15, 2020, 7:44am UTC](https://discourse.julialang.org/t/different-spawning-behavior-of-spawn-and-channel-spawn-true/48413/1 "2020-10-15T07:44:45Z")

</div>

`Channel(..., spawn=true)` seems to spawn not to all available threads. An MWE:

```julia
using .Threads

function tinfo(ch::Channel)
    from = take!(ch)
    put!(from, threadid())
end

me = Channel(10)

```

If I use `Threads.@spawn` to start the tasks …

```julia
for i in 1:nthreads()
    ch = Channel(1)
    Threads.@spawn tinfo(ch)
    yield()
    put!(ch, me)
end

julia> map(x->take!(me), 1:nthreads())
8-element Array{Int64,1}:
 2
 3
 7
 1
 6
 4
 5
 8

```

… I always get all threads as someone would expect.

If I do the same with `Channel(..., spawn=true)`, I don’t get all threads:

```julia
for i in 1:nthreads()
    ch = Channel(tinfo, 1, spawn=true)
    yield()
    put!(ch, me)
end

julia> map(x->take!(me), 1:nthreads())
8-element Array{Int64,1}:
 3
 2
 8
 3
 4
 8
 8
 8

```

This occurs most of the time. Only once with a fresh REPL I got all threads.

Can you confirm this on your machines? Is this behavior expected? Can I change it? Should I write an issue?

I’m on a Mac, Julia 1.5.2:

> **Versioninfo:**
>
> ```julia
> julia> versioninfo()
> Julia Version 1.5.2
> Commit 539f3ce943 (2020-09-23 23:17 UTC)
> Platform Info:
> OS: macOS (x86_64-apple-darwin18.7.0)
> CPU: Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
> WORD_SIZE: 64
> LIBM: libopenlibm
> LLVM: libLLVM-9.0.1 (ORCJIT, skylake)
> Environment:
> JULIA_NUM_THREADS = 8
> JULIA_EDITOR = "/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code"
> 
> ```

---

<div class="post-metadata">

### Author: ![tisztamo](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tisztamo/32/16200_2.png) [@tisztamo](https://discourse.julialang.org/u/tisztamo)
#### Post date: [October 15, 2020, 9:26am UTC](https://discourse.julialang.org/t/different-spawning-behavior-of-spawn-and-channel-spawn-true/48413/2 "2020-10-15T09:26:23Z")

</div>

I have experienced similar issues when I have started threads with `@spawn` that started their life with waiting for something.

I guess your issue may be related: the scheduler thinks that the threads are available and reuses them instead of round-robin-ing. For me it seemed that this is not a bug, although it was very annoying. I ended up using your solution from here:

> [@Lightweight tasks, Julia vs Elixir/OTP](https://discourse.julialang.org/t/lightweight-tasks-julia-vs-elixir-otp/35082/22):
>
> Now in concluding this journey, I provide the following function for executing functions on specified threads: function onthread(f::F, id::Int) where {F\<:Function} t = Task(nothing) @assert id in 1:nthreads() "thread $id not available!" @threads for i in 1:nthreads() if i == id t = @async f() end end fetch(t) end Anyone having single threaded applications involving tasks can speed things up with it. With the above (contrived) example you can do j…

---

<div class="post-metadata">

### Author: ![pbayer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pbayer/32/11675_2.png) [@pbayer](https://discourse.julialang.org/u/pbayer)
#### Post date: [October 15, 2020, 11:33am UTC](https://discourse.julialang.org/t/different-spawning-behavior-of-spawn-and-channel-spawn-true/48413/3 "2020-10-15T11:33:17Z")

</div>

Now it is a little bit different since I’m developing an [actor library](https://github.com/pbayer/YAActL.jl) and I want parallel actors to use all available threads.

I could use the solution you mentioned to start tasks on predetermined threads. But then I had to do the load-balancing myself, which I don’t want to. In a first approach `Threads.@spawn` seems to work for me but not `Channel(..., spawn=true)`.

But following the [documentation for `Channel`](https://docs.julialang.org/en/v1/base/parallel/#Base.Channel-Tuple%7BFunction%7D):

> If `spawn = true` , the Task created for `func` may be scheduled on another thread in parallel, equivalent to creating a task via [`Threads.@spawn`](https://docs.julialang.org/en/v1/base/multi-threading/#Base.Threads.@spawn).

there should be no difference between the two.

---

<div class="post-metadata">

### Author: ![tisztamo](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tisztamo/32/16200_2.png) [@tisztamo](https://discourse.julialang.org/u/tisztamo)
#### Post date: [October 15, 2020, 1:17pm UTC](https://discourse.julialang.org/t/different-spawning-behavior-of-spawn-and-channel-spawn-true/48413/4 "2020-10-15T13:17:12Z")

</div>

Yes, it seems that there is a difference (can confirm on Linux, v1.5.2)

It may be worth opening an issue, I would also be happy to see more deterministic scheduling behavior when having only a few threaded tasks.

On the other hand I am not sure that filling all threads with tasks as soon as possible is the best strategy for every situation, so promising it in Base would not be wise. Maybe that’s why the docs of `@spawn` says “any available thread”:

[https://docs.julialang.org/en/v1/base/multi-threading/#Base.Threads.@spawn](https://docs.julialang.org/en/v1/base/multi-threading/#Base.Threads.@spawn)

---

<div class="post-metadata">

### Author: ![pbayer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pbayer/32/11675_2.png) [@pbayer](https://discourse.julialang.org/u/pbayer)
#### Post date: [October 15, 2020, 5:42pm UTC](https://discourse.julialang.org/t/different-spawning-behavior-of-spawn-and-channel-spawn-true/48413/5 "2020-10-15T17:42:18Z")

</div>

After looking at the `Threads.@spawn` macro I guessed that the spawning may be linked to the scope where the macro is executed. If I execute the same for loop in a local scope, I get the same observed behavior as for `Channel(..., spawn=true)`:

```julia
function dospawn()
    for i in 1:nthreads()
        ch = Channel(1)
        Threads.@spawn tinfo(ch)
        yield()
        put!(ch, me)
    end
end    

julia> map(x->take!(me), 1:nthreads())
8-element Array{Int64,1}:
 2
 3
 7
 6
 4
 2
 7
 3

```
