# Why can Channels not be used by threads?

**URL:** <https://discourse.julialang.org/t/why-can-channels-not-be-used-by-threads/99353>\
**Category:** General Usage\
**Tags:** threads, channels\
**Created:** [May 24, 2023, 9:27pm UTC](https://discourse.julialang.org/t/why-can-channels-not-be-used-by-threads/99353 "2023-05-24T21:27:40Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![hsgg](https://avatars.discourse-cdn.com/v4/letter/h/838e76/32.png) [@hsgg](https://discourse.julialang.org/u/hsgg)\
**Post date:** [May 24, 2023, 9:27pm UTC](https://discourse.julialang.org/t/why-can-channels-not-be-used-by-threads/99353/1 "2023-05-24T21:27:40Z")

</div>

I am using Channels to synchronize and distribute workloads between threads (and it is working really well).

However, the documentation about [@threads](https://docs.julialang.org/en/v1/base/multi-threading/#Base.Threads.@threads) states that the assumptions that go into the threading model mean that

> Communicating between iterations using blocking primitives like `Channel`s is incorrect.

Why is this so? Why is it wrong to use channels with threads?

---

<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:** [May 24, 2023, 9:33pm UTC](https://discourse.julialang.org/t/why-can-channels-not-be-used-by-threads/99353/2 "2023-05-24T21:33:16Z")

</div>

if it’s “blocking”, it means two different iterations (also two different threads) can’t work at the same time, so what’s the point?

---

<div class="post-metadata">

**Author:** ![hsgg](https://avatars.discourse-cdn.com/v4/letter/h/838e76/32.png) [@hsgg](https://discourse.julialang.org/u/hsgg)\
**Post date:** [May 24, 2023, 9:46pm UTC](https://discourse.julialang.org/t/why-can-channels-not-be-used-by-threads/99353/3 "2023-05-24T21:46:40Z")

</div>

I mean, I have ensured that there are no race conditions or deadlocks, and the threads very much do work in parallel at the same time (the channels are used for a very small amount of time only in each iteration). So… did I just misread the documentation, and all it is saying is that you shouldn’t create deadlocks, race conditions, and need to be tiny bit careful that you still run in parallel? Because the way it is worded seems to suggest some deep technical incompatibility between threads and channels?

---

<div class="post-metadata">

**Author:** ![danielwe](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/danielwe/32/35657_2.png) [@danielwe](https://discourse.julialang.org/u/danielwe)\
**Post date:** [May 24, 2023, 10:11pm UTC](https://discourse.julialang.org/t/why-can-channels-not-be-used-by-threads/99353/4 "2023-05-24T22:11:24Z")

</div>

If I were to hazard a guess, the problem is that the `@threads` macro does not guarantee that all iterations will be scheduled simultaneously. Thus you could end up in a situation where iteration `i` calls `take!` on an empty channel, blocking until someone else calls `put!` on it—but iteration `j`, which is supposed to do the `put!`, won’t be scheduled until after iteration `i` is finished. Maybe the scheduler decided to run iterations `i` and `j` sequentially in the same task.

This is an issue specifically with the `@threads` macro, not with combining channels and threads in general. You should be safe if you replace

```julia
Threads.@threads for i in I
    # stuff
end

```

with

```julia
@sync for i in I
    Threads.@spawn begin
        # stuff
    end
end

```

---

<div class="post-metadata">

**Author:** ![hsgg](https://avatars.discourse-cdn.com/v4/letter/h/838e76/32.png) [@hsgg](https://discourse.julialang.org/u/hsgg)\
**Post date:** [May 24, 2023, 10:36pm UTC](https://discourse.julialang.org/t/why-can-channels-not-be-used-by-threads/99353/5 "2023-05-24T22:36:20Z")

</div>

Ah, thanks, that would make sense.

I guess, to ensure no deadlocks, I would need to know if two tasks happen to run on the same thread. That is, can I ensure that `nthreads()` tasks run on different threads each?

---

<div class="post-metadata">

**Author:** ![danielwe](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/danielwe/32/35657_2.png) [@danielwe](https://discourse.julialang.org/u/danielwe)\
**Post date:** [May 24, 2023, 11:31pm UTC](https://discourse.julialang.org/t/why-can-channels-not-be-used-by-threads/99353/6 "2023-05-24T23:31:43Z")

</div>

> [@hsgg](#):
>
> to ensure no deadlocks, I would need to know if two tasks happen to run on the same thread

No, this is not necessary. Tasks on the same thread can yield to each other just like any other asynchronous tasks. Tasks started with `Threads.@spawn` and `Threads.@threads [:dynamic]` are not even pinned to a specific thread, and can and will migrate between threads during execution.

The gotcha when using `@threads` is that it does not guarantee a one-to-one correspondence between tasks and loop body iterations, and it does not guarantee that all tasks are scheduled concurrently. It may combine multiple iterations into a single task, and it may choose to wait until some tasks are finished before starting others. That’s why (quoting the documentation) _The loop body for each iteration must be able to make forward progress independent of other iterations_.

> [@hsgg](#):
>
> can I ensure that `nthreads()` tasks run on different threads each?

If you spawn exactly `nthreads()` tasks with `Threads.@spawn`, chances are they will occupy one thread each.

---

<div class="post-metadata">

**Author:** ![Oscar\_Smith](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oscar_smith/32/25343_2.png) [@Oscar\_Smith](https://discourse.julialang.org/u/Oscar_Smith)\
**Post date:** [May 24, 2023, 11:32pm UTC](https://discourse.julialang.org/t/why-can-channels-not-be-used-by-threads/99353/7 "2023-05-24T23:32:28Z")

</div>

I think a lot of what you’re missing is that threads share memory so you don’t **need** channels. You can just use a regular array that you push and pop from (with appropriate locking).

---

<div class="post-metadata">

**Author:** ![danielwe](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/danielwe/32/35657_2.png) [@danielwe](https://discourse.julialang.org/u/danielwe)\
**Post date:** [May 24, 2023, 11:39pm UTC](https://discourse.julialang.org/t/why-can-channels-not-be-used-by-threads/99353/8 "2023-05-24T23:39:10Z")

</div>

> [@Oscar\_Smith](#):
>
> You can just use a regular array that you push and pop from (with appropriate locking).

Isn’t a `Channel` just this but with the locking built-in and automated? From what I can tell it’s made for exactly this purpose (message-passing and synchronization between asynchronous/multithreaded tasks—not to be confused with `RemoteChannel`, which is for use with multiprocessing). The manual array-and-lock approach runs into exactly the same issues with `@threads`.

---

<div class="post-metadata">

**Author:** ![jpsamaroo](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jpsamaroo/32/46804_2.png) [@jpsamaroo](https://discourse.julialang.org/u/jpsamaroo)\
**Post date:** [May 25, 2023, 3:59am UTC](https://discourse.julialang.org/t/why-can-channels-not-be-used-by-threads/99353/9 "2023-05-25T03:59:51Z")

</div>

Yes, but as you pointed out, it has blocking semantics, which are not always acceptable or desirable. `push!` and `pop!` on `Array` don’t block, and `pop!` throws if you try to remove an element from an empty array.
