Search code examples
c#async-awaitchannelsystem.threading.channels

Async method not executing when using System.Threading.Channels


For some reason, it appears code inside of the Consumer nor Producer Tasks is ever executed. Where am I going wrong?

using System.Threading.Channels;

namespace TEST.CHANNELS
{
    public class Program
    {
        public static async Task Main(string[] args)
        {
            var channel = Channel.CreateUnbounded<int>();
            var cancel = new CancellationToken();
            await Consumer(channel, cancel);
            await Producer(channel, cancel);

            Console.ReadKey();
        }

        private static async Task Producer(Channel<int, int> ch, CancellationToken cancellationToken)
        {
            for (int i = 0; i < 59; i++)
            {
                await Task.Delay(1000, cancellationToken);
                await ch.Writer.WriteAsync(i, cancellationToken);
            }
        }
        
        private static async Task Consumer(Channel<int, int> ch, CancellationToken cancellationToken)
        {
            await foreach (var item in ch.Reader.ReadAllAsync(cancellationToken))
            {
                Console.WriteLine(item);
            }
        }
    }
}

Solution

  • If you are new, I recommend reading Tutorial: Learn to debug C# code using Visual Studio. You should know how to put breakpoints to see your code running step-by-step.

    Now however since this one involves async/Task, it may looks confusing, but when you step in Consumer, you will see it stops at await foreach (var item in ch.Reader.ReadAllAsync(cancellationToken)) line.

    The reason is the consumer is waiting for something that producer never puts in. The reason is your first await put a stop to your code so the 2nd line never get executed.

    await Consumer(channel, cancel);
    await Producer(channel, cancel);
    

    This should fix the issue:

    var consumerTask = Consumer(channel, cancel);
    var producerTask = Producer(channel, cancel);
    
    await Task.WhenAll(consumerTask, producerTask);
    

    What the above code says is,

    1. Run Consumer Task, don't wait for it, but keep track of it in consumerTask.

    2. Run Producer Task, don't wait for it, but keep track of it in producerTask.

    3. Wait until both consumerTask and producerTask finishes using Task.WhenAll.

    Note that it seems you still have a logical problem with Consumer, since it will never exit so your ReadKey() will likely not getting hit (your app would stuck at the WhenAll line). I think it's much easier "practice" for you if you intend to fix it if it's a bug.