Closed AndreasHeisel closed 4 years ago
Define "in order": if you have multiple producers, even if the channel executes the writes in the exact same order as WriteAsync
was called, this isn't necessarily the same order as you're expecting. The reality is much more complicated, of course, and prone to wacky timing issues.
@Clockwork-Muse
I meant chronologically. This would (at least) guarantee that the calls of one producer will be executed in the exact order.
I know that there is some uncertainty if two producers call 'at the same time' due to thread scheduling and multicore issues. But apart from that?
Producers call WriteAsync multiple times without awaiting it.
Mmm it's not clear this statement if channel is bounded and full WritaAsync
will await
From Stephen's post https://devblogs.microsoft.com/dotnet/an-introduction-to-system-threading-channels/
However, ChannelWriter<T> also provides the WriteAsync method; in such a case where the channel is full and writing would need to wait (often referred to as “back pressure”),
WriteAsync can be used, with the producer awaiting the result of WriteAsync and only being allowed to continue when room becomes available.
@MarcoRossignoli Thanks for the link. I see the problem, but it is still interessting to know what would happen if one violates this.
if one violates this
What do you mean for violates?Producers are awaiting(light block...no thread if truly async) and consumer is draining items and signal producers when there is free space, so from the perspective of single producer item are ordered, but you don't know(and you cannot do assumptions) respect other producers.
with the producer awaiting the result of WriteAsync and only being allowed to continue when room becomes available
Calling WriteAsync "fire and forget" without awaiting the result.
Ah understood if you don't await "continuation" won't be setup so you don't have guarantee that not overlap with other WriteAsync
(on the same path) so you're not violating nothing from channel perspective is your code that declare this behaviour, you "forget" to await the result, you won't be sure in that case about ordering on that path(for path I'm talking about for instance a WriteAsync inside a foreach of some items in a collection).
BTW cc: @stephentoub that works on this and can confirm or reject my thought.
Exactly. I have a bounded channel because I need backpressure to prevent the system blowing up. In most cases this ist fine. But in one code path (a sync to async boundary) I can't afford to wait and also can't afford to loose the information. In this very rare situation it is better to exceed the boundary. The idea is, if the WriteAsync calls would queue up to use this as "emergency queue"
You could use a new bounded/unbounded sync write channel to offload item of this particular sync path and correctly await ReadAsync/WriteAsync
on consumer that write on "main" bounded channel, you should keep the order in this way. It's a basic TPL Dataflow scenario.
I know, but this doubles the memory footprint for a rare szenario. It's also difficult to maintain order between the entries in the unbounded channel an the ones directly insterted into the bounded channel.
Maybe a new feature: ChannelWriter.WriteForced(T item)
that ignore bounds :-)
Make it unbounded and use a senaphoreslim on the async path if you are really stuck. Making a bounded channel able to be unbounded sometimes means you lose some of the optimisations you can do in theory around the internal data structures.
If you call WriteAsync
in sequence, each call will complete in-order. If you call WriteAsync
concurrently, the order between the concurrent calls will not be defined (it depends on who gets a lock first).
However: if you're doing a fire-and-forget WriteAsync
, you're essentially just using WriteAsync
as its own extra queue. You've already computed the value, so using an unbounded queue will be far more efficient. If you don't need to guarantee the global ordering, consider feeding an unbounded "emergency queue" into a your normal bounded queue.
@scalablecory That is exactly what I tried to archieve. Just wanted to prove it.
Supposed I have a bounded Channel that is fed by multiple producers. This channel blocks because it is full. Producers call WriteAsync multiple times without awaiting it.
Will these writes be executed in order if the channel gets drained?