# Is there a possibility to prevent task switches in a block of code?

**URL:** <https://discourse.julialang.org/t/is-there-a-possibility-to-prevent-task-switches-in-a-block-of-code/106798>\
**Category:** Performance\
**Tags:** question, async\
**Created:** [November 27, 2023, 11:58am UTC](https://discourse.julialang.org/t/is-there-a-possibility-to-prevent-task-switches-in-a-block-of-code/106798 "2023-11-27T11:58:53Z")\
**Posts on this page:** 20\
**Page:** 2

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [November 27, 2023, 2:52pm UTC](https://discourse.julialang.org/t/is-there-a-possibility-to-prevent-task-switches-in-a-block-of-code/106798/21 "2023-11-27T14:52:26Z")

</div>

> [@schlichtanders](#):
>
> please link to the documentation which says so

please link to the documentation that says task-switching only happens to a limited list of functions. It doesn’t make any guarantee, because it’s hard and that’s precisely the point here.

---

<div class="post-metadata">

**Author:** ![schlichtanders](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/schlichtanders/32/32145_2.png) [@schlichtanders](https://discourse.julialang.org/u/schlichtanders)\
**Post date:** [November 27, 2023, 2:56pm UTC](https://discourse.julialang.org/t/is-there-a-possibility-to-prevent-task-switches-in-a-block-of-code/106798/22 "2023-11-27T14:56:37Z")

</div>

you are argument seems to go like this:  
“you cannot disprove me, hence I am right”

just because task-switching is difficult (I don’t know whether it is difficult actually), it still does not mean that Julia works like you are saying

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [November 27, 2023, 2:59pm UTC](https://discourse.julialang.org/t/is-there-a-possibility-to-prevent-task-switches-in-a-block-of-code/106798/23 "2023-11-27T14:59:33Z")

</div>

> [@schlichtanders](#):
>
> the link is not about async tasks, but about Threads

> [@schlichtanders](#):
>
> this seems to indicate that async task can indeed not be interrupted anywhere, but just at certain points like `sleep`

I thought thread spawning was based on tasks so task-related issues should apply to both. The blogpost seems to affirm that yielding does happen at deterministic points, it’s just hard to keep track of so many independent factors, let alone make any guarantee.

> [@](#):
>
> [Don’t try to reason about yielding](https://julialang.org/blog/2023/07/PSA-dont-use-threadid/#fndef:yielding)
> 
> Many existing uses of thread local state happen to be relatively robust and give correct answers only because the functions they are calling during execution do not yield. One may then think “well, I can just avoid this problem by making sure my code doesn’t yield”, but we think this is a bad and unsustainable idea, because whether or not a function call will yield is not stable, obvious, or easily inspectable.
> 
> For instance, if a function `f` is updated to include a background `@debug` statement or other forms of non-user-visible IO, it may change from being non-yielding to yielding. If during a call to `f`, the compiler encounters a dynamic dispatch where new code must be JIT compiled, a yield-point may be encountered, and any number of other internal changes could happen to code which can cause it to yield.

> [@schlichtanders](#):
>
> I could build a Cassette Meta transformer which replaces all invokations to sleep with invokations to Base.Libc.systemsleep

Could that go deep enough to remove all task switches in general in a block? Or could that break printing and such?

---

<div class="post-metadata">

**Author:** ![schlichtanders](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/schlichtanders/32/32145_2.png) [@schlichtanders](https://discourse.julialang.org/u/schlichtanders)\
**Post date:** [November 27, 2023, 3:03pm UTC](https://discourse.julialang.org/t/is-there-a-possibility-to-prevent-task-switches-in-a-block-of-code/106798/24 "2023-11-27T15:03:26Z")

</div>

Thank you so much for quoting these important passages! I haven’t read them indeed.

My usecase is to interact with git - i.e. some external system.

So I guess the julian approach to make sure that several git commands are actually executed one after the other is to have a global git lock which you acquire and release.

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [November 27, 2023, 3:04pm UTC](https://discourse.julialang.org/t/is-there-a-possibility-to-prevent-task-switches-in-a-block-of-code/106798/25 "2023-11-27T15:04:08Z")

</div>

> [@schlichtanders](#):
>
> you are argument seems to go like this:  
> “you cannot disprove me, hence I am right”

sorry if that sounds like my argument and sorry for not being expert enough to the Julia source code 🙂

There are no documentation, only discussions I recall and Julia source code if you want “proof”, I will defer this question to other experts you may find more trustworthy than me.

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [November 27, 2023, 3:05pm UTC](https://discourse.julialang.org/t/is-there-a-possibility-to-prevent-task-switches-in-a-block-of-code/106798/26 "2023-11-27T15:05:05Z")

</div>

> [@schlichtanders](#):
>
> My usecase is to interact with git - i.e. some external system.

```julia
julia> f(i) = (read(`git --version`, String); return i)

julia> read(`git --version`, String)
"git version 2.43.0\n"

julia> let state = zeros(Int, Threads.nthreads()), N=100
           @sync for i ∈ 1:N
               @async state[Threads.threadid()] += f(i)
           end
           sum(state), sum(1:N)
       end
(100, 5050)

```

---

<div class="post-metadata">

**Author:** ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)\
**Post date:** [November 27, 2023, 3:08pm UTC](https://discourse.julialang.org/t/is-there-a-possibility-to-prevent-task-switches-in-a-block-of-code/106798/27 "2023-11-27T15:08:49Z")

</div>

> [@schlichtanders](#):
>
> My usecase is to interact with git - i.e. some external system.
> 
> So I guess the julian approach to make sure that several git commands are actually executed one after the other is to have a global git lock which you acquire and release.

This isn’t really a Julia specific issue, this is a regular concurrency problem. You have a shared resource (the external git repo) that must not be modified concurrently. I don’t know whether git itself protects that resource or not (likely not, since it’s fair to assume that only one `git` invocation runs at a given time), so to be safe, you’d need a lock or other exclusive-ownership mechanism in any language. Not to mention that tasks waiting on the result of an external command by default wait and thus `yield` to other tasks, thereby allowing other tasks to spawn their own `git` command that they then wait on.

There are a number of such concurrency problems that are NOT caused by multithreading, but rather only exposed more easily by it. The underlying issue is protection of that shared resource when two or more tasks access the resource concurrently, which is not necessarily the same as simultaneously.

---

<div class="post-metadata">

**Author:** ![schlichtanders](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/schlichtanders/32/32145_2.png) [@schlichtanders](https://discourse.julialang.org/u/schlichtanders)\
**Post date:** [November 27, 2023, 3:12pm UTC](https://discourse.julialang.org/t/is-there-a-possibility-to-prevent-task-switches-in-a-block-of-code/106798/28 "2023-11-27T15:12:40Z")

</div>

Let me summarize what I learned:

- it could potentially be possible to write something which prevents task switches, as not every julia function yields, but quite a lot (maybe there is some underlying taskswith method called)
- the general julia recommendation is to not rely on such potential `prevent_taskswitch` logic (which does not exist yet), but rather write code in a way which is agnostic to taskswitches

For the application of interacting with external systems it still might be helpful to have a `prevent_taskswitch` wrapper.

As long as this does not exist the only alternative I can think of is locking.

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [November 27, 2023, 3:14pm UTC](https://discourse.julialang.org/t/is-there-a-possibility-to-prevent-task-switches-in-a-block-of-code/106798/29 "2023-11-27T15:14:01Z")

</div>

Pretty much yea. Although you might also be interested in `task_local_storage` depending on what your application is.

---

<div class="post-metadata">

**Author:** ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)\
**Post date:** [November 27, 2023, 3:14pm UTC](https://discourse.julialang.org/t/is-there-a-possibility-to-prevent-task-switches-in-a-block-of-code/106798/30 "2023-11-27T15:14:37Z")

</div>

It’s a bit more subtle here actually, because `@async` does something nasty to avoid task migration. See in particular:

```julia
help?> @async
  @async

  Wrap an expression in a Task and add it to the local machine's scheduler queue.

  Values can be interpolated into @async via $, which copies the value directly into the
  constructed underlying closure. This allows you to insert the value of a variable, isolating
  the asynchronous code from changes to the variable's value in the current task.

  │ Warning
  │
  │ It is strongly encouraged to favor Threads.@spawn over @async always even when no
  │ parallelism is required especially in publicly distributed libraries. This is
  │ because a use of @async disables the migration of the parent task across worker
  │ threads in the current implementation of Julia. Thus, seemingly innocent use of
  │ @async in a library function can have a large impact on the performance of very
  │ different parts of user applications.

```

So `@async` actually does protect from task migration in a way that `@spawn` doesn’t, but it’s also not as good at this as the docstring warning might have one beleive (as @jling showed via `sleep`), but such guard rails should not be trusted anyways.

It’s like running into a window on a high building and saying "what’s the problem? the window will catch me. "

---

<div class="post-metadata">

**Author:** ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)\
**Post date:** [November 27, 2023, 3:15pm UTC](https://discourse.julialang.org/t/is-there-a-possibility-to-prevent-task-switches-in-a-block-of-code/106798/31 "2023-11-27T15:15:58Z")

</div>

> [@schlichtanders](#):
>
> As long as this does not exist the only alternative I can think of is locking.

I beg you to just read the blogpost. We cover this exactly

---

<div class="post-metadata">

**Author:** ![schlichtanders](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/schlichtanders/32/32145_2.png) [@schlichtanders](https://discourse.julialang.org/u/schlichtanders)\
**Post date:** [November 27, 2023, 3:17pm UTC](https://discourse.julialang.org/t/is-there-a-possibility-to-prevent-task-switches-in-a-block-of-code/106798/32 "2023-11-27T15:17:11Z")

</div>

I went over the blogpost and it recommends task local state to solve the problem

that is just not a possibility when interacting with an external system like git

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [November 27, 2023, 3:17pm UTC](https://discourse.julialang.org/t/is-there-a-possibility-to-prevent-task-switches-in-a-block-of-code/106798/33 "2023-11-27T15:17:39Z")

</div>

> [@Mason](#):
>
> because `@async` does something nasty to avoid task migration.

Wait, I thought task migration was about a task being scheduled on a possibly different thread when restarted. Even if it doesn’t migrate, pausing and restarting are still task switches, right? Or am I misunderstanding things horribly?

Oh nvm, it just clicked, the topic there is thread-local state, not task switches.

---

<div class="post-metadata">

**Author:** ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)\
**Post date:** [November 27, 2023, 3:21pm UTC](https://discourse.julialang.org/t/is-there-a-possibility-to-prevent-task-switches-in-a-block-of-code/106798/34 "2023-11-27T15:21:43Z")

</div>

> [@Benny](#):
>
> Wait, I thought task migration was about a task being scheduled on a possibly different thread when restarted. Even if it doesn’t migrate, pausing and restarting are still task switches, right? Or am I misunderstanding things horribly

This is why in the original version of the blogpost, I wanted to demonstrate the bug with `@async`, not `@spawn`, but we went with `@spawn` because we explciitly document that we don’t want users to use `@asnyc` ~~`@spawn`~~. In the current iteration of the blogpost, you’ll notice however that we demonstrate the bug using `julia --threads=1`.

That’s because multithreading is actually irrelevant to this problem. It’s a concurrency problem, not a problem directly to do with multithreading. Multithreading just makes it manifest more because people assume that the number of concurrent tasks will equal the number of threads, and that tasks won’t migrate to new ~~task~~ thread IDs

---

<div class="post-metadata">

**Author:** ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)\
**Post date:** [November 27, 2023, 3:22pm UTC](https://discourse.julialang.org/t/is-there-a-possibility-to-prevent-task-switches-in-a-block-of-code/106798/35 "2023-11-27T15:22:30Z")

</div>

> [@Mason](#):
>
> and that tasks won’t migrate to new task IDs

I think you mean thread IDs there 😉

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [November 27, 2023, 3:23pm UTC](https://discourse.julialang.org/t/is-there-a-possibility-to-prevent-task-switches-in-a-block-of-code/106798/36 "2023-11-27T15:23:16Z")

</div>

> [@Mason](#):
>
> This is why in the original version of the blogpost, I wanted to demonstrate the bug with `@async`, not `@spawn`, but we went with `@spawn` because we explciitly document that we don’t want users to use `@spawn`.

Wait, do you mean “don’t want users to use `@async`” there? That’s what the blogpost reads to me

---

<div class="post-metadata">

**Author:** ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)\
**Post date:** [November 27, 2023, 3:23pm UTC](https://discourse.julialang.org/t/is-there-a-possibility-to-prevent-task-switches-in-a-block-of-code/106798/37 "2023-11-27T15:23:31Z")

</div>

> [@Sukera](#):
>
> I think you mean thread IDs there 😉

oops, yeah

---

<div class="post-metadata">

**Author:** ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)\
**Post date:** [November 27, 2023, 3:24pm UTC](https://discourse.julialang.org/t/is-there-a-possibility-to-prevent-task-switches-in-a-block-of-code/106798/38 "2023-11-27T15:24:10Z")

</div>

> [@Benny](#):
>
> Wait, do you mean discouraging “don’t want users to use `@async`” there? That’s what the blogpost reads to me

The julia docs have explictly said not to use `@asnyc` anywhere for a long time now. It only exists for backwards compatibility.

---

<div class="post-metadata">

**Author:** ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)\
**Post date:** [November 27, 2023, 3:24pm UTC](https://discourse.julialang.org/t/is-there-a-possibility-to-prevent-task-switches-in-a-block-of-code/106798/39 "2023-11-27T15:24:18Z")

</div>

> [@Benny](#):
>
> Even if it doesn’t migrate, pausing and restarting are still task switches, right? Or am I misunderstanding things horribly?

You can pause & restart your task as much as you want, as long as you protect your shared resource from other tasks that would also like to modify that resource for as long as you need it to be protected for. If that means hiding the resource behind a lock until the original task exits, then that’s what you need to do.

---

<div class="post-metadata">

**Author:** ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)\
**Post date:** [November 27, 2023, 3:24pm UTC](https://discourse.julialang.org/t/is-there-a-possibility-to-prevent-task-switches-in-a-block-of-code/106798/40 "2023-11-27T15:24:52Z")

</div>

> [@schlichtanders](#):
>
> I went over the blogpost and it recommends task local state to solve the problem
> 
> that is just not a possibility when interacting with an external system like git

That is simply untrue. If you post a dummy example we can help show you how to use concurrency-safe patterns here.

[Previous page](https://discourse.julialang.org/t/is-there-a-possibility-to-prevent-task-switches-in-a-block-of-code/106798.md?page=1)

[Next page](https://discourse.julialang.org/t/is-there-a-possibility-to-prevent-task-switches-in-a-block-of-code/106798.md?page=3)
