Search code examples
c#system.threading.channels

How to store a Channel in a Concurrent Dictionary


A rather tricky one here but the concept I'm trying to fathom is fairly simple.

I have a public static concurrent dictionary, accessible from other classes and threads:

public static readonly ConcurrentDictionary<int, object> tcpServerChannelDictionary = new ConcurrentDictionary<int, object>();

I have one particular background thread where I initially create a new C# Channel and store the instance inside the dictionary. Within this same thread (in the same method) I then start to wait for messages asynchronously. Have removed some unnecessary code bloat from the method:

var myChannel = Channel.CreateUnbounded<string>();
            tcpServerChannelDictionary.TryAdd(int, myChannel);

await foreach (var data in myChannel.Reader.ReadAllAsync())
{
    // Do something with message
}

First stumbling block was I couldn't define the static dictionary with the "object" as a channel i.e. ConcurrentDictionary<int, Channel>... becuase I'd get a warning sign that "Channel: Static Types cannot be used as type arguments" so this is why I declared an object type instead.

This appeared to work without VS code warnings until I then try to access the dictionary object from another class (and what will be another thread):

if (TcpServer.tcpServerChannelDictionary.TryGetValue(int, out Channel myChannel))
{
   await myChannel.Writer.WriteAsync(data);
}

enter image description here

I somehow need to either cast the object to the channel I'm trying to acess so i can write a message to it, or some other solution that prevents the warnings. I'm new to channels but saw immediately how powerful using them will be.


Solution

  • The Channel class is a helper static class with factory methods on it. All of those methods return a Channel<T>. In your case you are creating Channel<string> objects, and that's a type that a Dictionary can be constructed from.

    public static readonly ConcurrentDictionary<int, Channel<string>> tcpServerChannelDictionary = new();
    //... 
    if (TcpServer.tcpServerChannelDictionary.TryGetValue(int, out var /*Channel<string>*/ myChannel)) 
    {
       await myChannel.Writer.WriteAsync(data);
    }
    

    Now if for whatever reason you still needed to use the object type for the values of your dictionary (maybe you have different generic parameters for Channel<T> and you want them all in the same collection) your out variable still needs to be object and then you need to cast it:

    if (TcpServer.tcpServerChannelDictionary.TryGetValue(int, out var /*object*/ oChannel) && oChannel is Channel<string> myChannel)
    {
       await myChannel.Writer.WriteAsync(data);
    }