# Is the operation scheduling a task blocking?

**URL:** <https://discourse.julialang.org/t/is-the-operation-scheduling-a-task-blocking/133372>\
**Category:** General Usage\
**Tags:** question, async\
**Created:** [October 23, 2025, 7:17am UTC](https://discourse.julialang.org/t/is-the-operation-scheduling-a-task-blocking/133372 "2025-10-23T07:17:44Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![WalterMadelim](https://avatars.discourse-cdn.com/v4/letter/w/3e96dc/32.png) [@WalterMadelim](https://discourse.julialang.org/u/WalterMadelim)\
**Post date:** [October 23, 2025, 7:17am UTC](https://discourse.julialang.org/t/is-the-operation-scheduling-a-task-blocking/133372/1 "2025-10-23T07:17:44Z")

</div>

Here is the doc [Asynchronous Programming · The Julia Language](https://docs.julialang.org/en/v1/manual/asynchronous-programming/#Basic-Task-operations)

> It is common to want to create a task and schedule it right away, so the macro [`Threads.@spawn`](https://docs.julialang.org/en/v1/base/multi-threading/#Base.Threads.@spawn) is provided for that purpose –- `Threads.@spawn x` is equivalent to `task = @task x; task.sticky = false; schedule(task)` .

I don’t understand the “is equivalent to” assertion, considering this example

```julia
julia> const v = collect(1:99999);

julia> const u = collect(1:99999);

julia> function busy(v)
           for i = 1:999999
               circshift!(v, rand(Int))
               reverse!(v)
           end
       end;

julia> Threads.@spawn busy(v); # this will return immediately so a new "julia>" can be seen

julia> t = @task busy(u);

julia> t.sticky = false;

julia> schedule(t); # this gets stuck
attempt to type some words, julia has no response

```

tested with JULIA\_NUM\_THREADS = 255,1

---

<div class="post-metadata">

**Author:** ![sgaure](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sgaure/32/14779_2.png) [@sgaure](https://discourse.julialang.org/u/sgaure)\
**Post date:** [October 23, 2025, 8:07am UTC](https://discourse.julialang.org/t/is-the-operation-scheduling-a-task-blocking/133372/2 "2025-10-23T08:07:04Z")

</div>

The documentation is slightly outdated. If you look at

```julia-auto
@macroexpand Threads.@spawn busy(v)

```

you’ll see that `@spawn` also sets the threadpool.  
Before you schedule the `t` task, you may look at its threadpool:

```julia-auto
julia> Threads.threadpool(t)
:interactive

```

So it runs on the same threadpool as your REPL, with a single thread. And it never yields.

---

<div class="post-metadata">

**Author:** ![WalterMadelim](https://avatars.discourse-cdn.com/v4/letter/w/3e96dc/32.png) [@WalterMadelim](https://discourse.julialang.org/u/WalterMadelim)\
**Post date:** [October 23, 2025, 8:44am UTC](https://discourse.julialang.org/t/is-the-operation-scheduling-a-task-blocking/133372/3 "2025-10-23T08:44:41Z")

</div>

But it gets stuck, even if I start with more than one interactive threads. I don’t understand.

```julia-auto
❯ julia --threads=1,4
               _
   _ _ _(_)_ | Documentation: https://docs.julialang.org
  (_) | (_) (_) |
   _ _ _| |_ __ _ | Type "?" for help, "]?" for Pkg help.
  | | | | | | |/ _` | |
  | | |_| | | | (_| | | Version 1.12.1 (2025-10-17)
 _/ |\ __'_|_|_|\__'_| | Official https://julialang.org release
|__/ |

julia> function busy(v)
           for i = 1:typemax(Int)
               circshift!(v, 7)
               reverse!(v)
           end
       end;

julia> t = @task busy(collect(1:1000))
Task (runnable) @0x00007a9653bfca60

julia> schedule(t) # gets stuck

```

I think the task can at most block one thread, isn’t it?

---

<div class="post-metadata">

**Author:** ![foobar\_lv2](https://avatars.discourse-cdn.com/v4/letter/f/ee59a6/32.png) [@foobar\_lv2](https://discourse.julialang.org/u/foobar_lv2)\
**Post date:** [October 23, 2025, 10:03am UTC](https://discourse.julialang.org/t/is-the-operation-scheduling-a-task-blocking/133372/4 "2025-10-23T10:03:38Z")

</div>

Oh my god, this is terrible. I would consider this an extremely annoying and very breaking bug.

Do you want to open an issue or should I? (you discovered the bug, after all)

```julia-auto
$ julia +1.12 -t 4 -e "t=Task(()->Threads.threadid()); t.sticky=false; schedule(t); println(fetch(t))"
1
$ julia +1.11 -t 4 -e "t=Task(()->Threads.threadid()); t.sticky=false; schedule(t); println(fetch(t))"
3

```

I think the responsible PR is [Fix `enq_work` behavior when single-threaded by kpamnany · Pull Request #48702 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/pull/48702)

PS. It is breaking because up to julia 1.11, the official and documented API for spawning a task in the threadpool was `t=Task(fun); t.sticky=false; #=do some other setup =# schedule(t);`.

Now, 1.12 silently changed the behavior (without deprecation warnings, not even with documentation update, such that current docs are simply wrong) such that this task now sticks to the threadpool. Hence, lots of old code will now silently degrade in performance / latency, in a way that unit tests are unlikely to notice. And some 1.12-era code will rely on the new (imo terrible) behavior for correctness 😱

Sticky-ness now has 3 values: Stick-to-thread, Stick-to-Threadpool, and unsticky, while we previously only had 2 values: Stick-to-thread and unsticky, and the update silently mapped unsticky-\>Stick-to-threadpool.

---

<div class="post-metadata">

**Author:** ![WalterMadelim](https://avatars.discourse-cdn.com/v4/letter/w/3e96dc/32.png) [@WalterMadelim](https://discourse.julialang.org/u/WalterMadelim)\
**Post date:** [October 23, 2025, 10:18am UTC](https://discourse.julialang.org/t/is-the-operation-scheduling-a-task-blocking/133372/5 "2025-10-23T10:18:39Z")

</div>

> [@foobar\_lv2](#):
>
> Do you want to open an issue or should I?

you are more experienced, go ahead then.

---

<div class="post-metadata">

**Author:** ![sgaure](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sgaure/32/14779_2.png) [@sgaure](https://discourse.julialang.org/u/sgaure)\
**Post date:** [October 23, 2025, 10:27am UTC](https://discourse.julialang.org/t/is-the-operation-scheduling-a-task-blocking/133372/6 "2025-10-23T10:27:15Z")

</div>

Hmm, that’s an unfortunate behaviour. It has to do with the new default for the interactive thread pool.

```julia-auto
$ julia +1.12 -t 4,0 -e "t=Task(()->(Threads.threadpool(), Threads.threadid())); t.sticky=false; schedule(t); println(fetch(t))"
(:default, 2)
$ julia +1.12 -t 4 -e "t=Task(()->(Threads.threadpool(), Threads.threadid())); t.sticky=false; schedule(t); println(fetch(t))"
(:interactive, 1)
$ julia +1.11 -t 4 -e "t=Task(()->(Threads.threadpool(), Threads.threadid())); t.sticky=false; schedule(t); println(fetch(t))"
(:default, 2)

```

---

<div class="post-metadata">

**Author:** ![foobar\_lv2](https://avatars.discourse-cdn.com/v4/letter/f/ee59a6/32.png) [@foobar\_lv2](https://discourse.julialang.org/u/foobar_lv2)\
**Post date:** [October 23, 2025, 10:42am UTC](https://discourse.julialang.org/t/is-the-operation-scheduling-a-task-blocking/133372/7 "2025-10-23T10:42:07Z")

</div>

Issue: [Unsticky Tasks stick to threadpool since 1.12 · Issue #59936 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/issues/59936)

---

<div class="post-metadata">

**Author:** ![WalterMadelim](https://avatars.discourse-cdn.com/v4/letter/w/3e96dc/32.png) [@WalterMadelim](https://discourse.julialang.org/u/WalterMadelim)\
**Post date:** [October 23, 2025, 11:06am UTC](https://discourse.julialang.org/t/is-the-operation-scheduling-a-task-blocking/133372/8 "2025-10-23T11:06:22Z")

</div>

Sorry, I still don’t understand the cause in my #3 post: I had 4 interactive threads and I was trying to executing only one task. 🙃

* * *

I also don’t understand this

```julia-auto
❯ julia --threads=1,0
               _
   _ _ _(_)_ | Documentation: https://docs.julialang.org
  (_) | (_) (_) |
   _ _ _| |_ __ _ | Type "?" for help, "]?" for Pkg help.
  | | | | | | |/ _` | |
  | | |_| | | | (_| | | Version 1.12.1 (2025-10-17)
 _/ |\ __'_|_|_|\__'_| | Official https://julialang.org release
|__/ |

julia> Threads.@spawn(:interactive, println("execute in interactive pool"))
execute in interactive pool
Task (done) @0x00007e85bd1ff2b0

julia> 

```

I have 0 interactive threads, then how is that task get done???

* * *

Also want to ask: what is the difference of the default pool and interactive pool in terms of performance? Is a task specified to be done within the interactive pool slower?

---

<div class="post-metadata">

**Author:** ![sgaure](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sgaure/32/14779_2.png) [@sgaure](https://discourse.julialang.org/u/sgaure)\
**Post date:** [October 23, 2025, 12:50pm UTC](https://discourse.julialang.org/t/is-the-operation-scheduling-a-task-blocking/133372/9 "2025-10-23T12:50:11Z")

</div>

> [@WalterMadelim](#):
>
> I have 0 interactive threads, then how is that task get done???
> 
> * * *
> 
> Also want to ask: what is the difference of the default pool and interactive pool in terms of performance? Is a task specified to be done within the interactive pool slower?

So, if you don’t have an interactive thread pool, you only have the `:default` pool. This was the default in 1.11. Everything, including the REPL, in the same thread pool. This leads to difficulties when running interactively. If you spawn `nthreads()` tasks, the REPL must wait until one of them finishes or calls `yield()`. Compute bound tasks typically don’t call `yield()`, but io functions, and `wait` and `lock` etc. may do that.

In 1.12, the default is to have an interactive thread pool with 1 thread. The same problem as in 1.11 then occurs if you schedule a compute bound task in the `:interactive` pool. This is to be expected.

However, if you have an interactive pool with more than 1 thread, it should be possible to schedule a compute-bound task to the interactive pool. When calling `schedule` on a task, a thread in the thread pool is picked for starting the task, and it may only change thread when it calls `yield()`. I think, in your example, that the initial thread where the task is started happens to be the same thread which the REPL runs in, and the REPL is sticky, and can’t run in another thread. So it’s blocked by the task it has scheduled.

I guess that if you have a `yield()` before the loop in `busy`, it may not block the REPL.

---

<div class="post-metadata">

**Author:** ![WalterMadelim](https://avatars.discourse-cdn.com/v4/letter/w/3e96dc/32.png) [@WalterMadelim](https://discourse.julialang.org/u/WalterMadelim)\
**Post date:** [October 23, 2025, 12:58pm UTC](https://discourse.julialang.org/t/is-the-operation-scheduling-a-task-blocking/133372/10 "2025-10-23T12:58:27Z")

</div>

I did some tests, the behavior is random.

```julia-auto
❯ julia --threads=1,2  
               _
   _ _ _(_)_ | Documentation: https://docs.julialang.org
  (_) | (_) (_) |
   _ _ _| |_ __ _ | Type "?" for help, "]?" for Pkg help.
  | | | | | | |/ _` | |
  | | |_| | | | (_| | | Version 1.12.1 (2025-10-17)
 _/ |\ __'_|_|_|\__'_| | Official https://julialang.org release
|__/ |

julia> function busy(v)
           for i = 1:typemax(Int)
               circshift!(v, 7)
               reverse!(v)
           end
       end;

julia> t = @task busy(collect(1:1000))
Task (runnable) @0x00007c217fffca60

julia> t.sticky = false
false

julia> schedule(t)
Task (runnable) @0x00007c217fffca60

julia> 

```

If I use more than one interactive threads as in this case, then it may gets stuck or maynot (as in the picture).

* * *

> [@sgaure](#):
>
> I think, in your example, that the initial thread where the task is started happens to be the same thread which the REPL runs in

I agree.

* * *

Another question: is it meaningful to have more than one interactive thread? Is only one interactive thread sufficient?

* * *

I think there is no big problem if users use only `Threads.@spawn` to do async tasks and correctly specify the `(default_num, interactive_num)` parameters, is it true? @foobar_lv2

---

<div class="post-metadata">

**Author:** ![sgaure](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sgaure/32/14779_2.png) [@sgaure](https://discourse.julialang.org/u/sgaure)\
**Post date:** [October 23, 2025, 1:09pm UTC](https://discourse.julialang.org/t/is-the-operation-scheduling-a-task-blocking/133372/11 "2025-10-23T13:09:40Z")

</div>

> [@WalterMadelim](#):
>
> If I use more than one interactive threads as in this case, then it may gets stuck or maynot (as in the picture).

Yes, and it happens in both 1.11 and 1.12. It’s unfortunate, but not new.

---

<div class="post-metadata">

**Author:** ![sgaure](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sgaure/32/14779_2.png) [@sgaure](https://discourse.julialang.org/u/sgaure)\
**Post date:** [October 23, 2025, 1:16pm UTC](https://discourse.julialang.org/t/is-the-operation-scheduling-a-task-blocking/133372/12 "2025-10-23T13:16:00Z")

</div>

> [@WalterMadelim](#):
>
> Another question: is it meaningful to have more than one interactive thread? Is only one interactive thread sufficient?

One could imagine that one wants a computation to utilize all the `:default` threads, while one experiments interactively with some threading, but stay away from the REPL thread. That’s difficult. An improvement would be if it was allowed with more thread pools, then one could fix the interactive pool to one thread, and pick and choose among the other thread pools. I know of some climate models which could benefit from such a partitioning. However, things like that would probably be better to run with `Distributed`. (or MPI.jl, which is the de facto standard for them).
