Non Blocking Channel Operations in C#

Our program demonstrates non-blocking channel operations. Here’s the full source code:

using System;
using System.Threading.Tasks;
using System.Threading.Channels;

class Program
{
    static async Task Main()
    {
        var messages = Channel.CreateUnbounded<string>();
        var signals = Channel.CreateUnbounded<bool>();

        // Here's a non-blocking receive. If a value is
        // available on `messages` then it will be received.
        // If not, it will immediately take the default case.
        if (messages.Reader.TryRead(out var msg))
        {
            Console.WriteLine($"received message {msg}");
        }
        else
        {
            Console.WriteLine("no message received");
        }

        // A non-blocking send works similarly. Here `msg`
        // cannot be sent to the `messages` channel, because
        // the channel has no receiver.
        // Therefore, the TryWrite will return false.
        msg = "hi";
        if (messages.Writer.TryWrite(msg))
        {
            Console.WriteLine($"sent message {msg}");
        }
        else
        {
            Console.WriteLine("no message sent");
        }

        // We can use multiple checks to implement a multi-way
        // non-blocking select. Here we attempt non-blocking
        // receives on both `messages` and `signals`.
        if (messages.Reader.TryRead(out msg))
        {
            Console.WriteLine($"received message {msg}");
        }
        else if (signals.Reader.TryRead(out var sig))
        {
            Console.WriteLine($"received signal {sig}");
        }
        else
        {
            Console.WriteLine("no activity");
        }
    }
}

To run the program, compile and execute it:

$ dotnet run
no message received
no message sent
no activity

This example demonstrates how to perform non-blocking operations on channels in C#. While C# doesn’t have built-in select statements for channels like in some other languages, we can achieve similar functionality using the TryRead and TryWrite methods of the Channel<T> class.

The Channel<T> class in C# provides a way to transfer data between producers and consumers asynchronously. It’s part of the System.Threading.Channels namespace and offers a more modern approach to producer-consumer scenarios compared to traditional blocking collections.

In this example, we create unbounded channels for messages and signals. We then demonstrate non-blocking receives and sends using the TryRead and TryWrite methods. These methods immediately return true if the operation was successful, or false if it would have blocked.

The multi-way select is simulated by using multiple if-else statements, checking each channel in turn. This approach allows us to check multiple channels without blocking on any of them.

Remember that while this example demonstrates non-blocking operations, in real-world scenarios you might want to consider using await with ReadAsync and WriteAsync for truly asynchronous operations, especially when dealing with I/O-bound tasks.