0

I am currently working with a Channels construct (like go routine) to manage multi-threaded state synchronization.

I'm curious as to the best practice on how to handle different operations for state management using that channel. One operation could be retrieval, and another could be to re-load that state from disk.

I'm feeling quite a bit of friction and complexity in supporting multiple operations using a single channel with parameter support for those operations. Specifically supporting different return types and the casting inside each operation decision path within that channel.

Can someone recommend some alternative ways or is this concept sufficient?

Here's an example:


private static readonly Dictionary<string, OmaObject> State = new Dictionary<string, OmaObject>(); 
private static readonly ISpookyHashV2 SpookyHashV2 = SpookyHashV2Factory.Instance.Create();

 public static ChannelReader<T> CreateScheduleMessenger<T>(ChannelReader<T> channelReader,
           Command command)
        {
            var output = Channel.CreateUnbounded<T>();
            
            Task.Run(async () =>
            {
                
                if (command is Command.Get)
                {
                        var raw = await channelReader.ReadAsync();
                        OmaObject omaObject = (OmaObject)Convert.ChangeType(raw, typeof(OmaObject));

                        var hash = SpookyHashV2.ComputeHash($"{omaObject.Customer}{omaObject.Device}{omaObject.TestName}").AsBase64String();
                        if (!State.ContainsKey(hash))
                            State.Add(hash, omaObject);

                        Console.WriteLine($"Schedule state count: {State.Count}");
                        await output.Writer.WriteAsync((T)omaObject);

                    
                } else if (command is Command.Delete)
                {
                        var omaObject = await channelReader.ReadAsync();
                        State.Clear();
                        Console.WriteLine($"State has been cleaned: {State.Count}");

                        await output.Writer.WriteAsync(true);
                        
                }
            
            });
            
            output.Writer.Complete();

            return output;
 }
Adrian
  • 42,911
  • 6
  • 107
  • 99
Dane Balia
  • 5,271
  • 5
  • 32
  • 57
  • The `go routine` construct is `Task` (or rather the combination of `await` and `Task`), not Channel. Channel is the pub/sub container, in both languages. A single-fire go routine in C# would be a simple `await Task.Run(..)` without requiring channels – Panagiotis Kanavos Sep 11 '20 at 13:57
  • 1
    If you don't loop to read all messages, there's not much reason to use a `Channel` at all. It's just a container. You feel friction because you're concentrating on the wrong construct. There's nothing asynchronous in this code, so it could be replaced by a simple method that just reads an input and provides some output. If you wanted to calculate a hash asynchronously, like [ComputeHashAsync does](https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.hashalgorithm.computehashasync?view=net-5.0), you'd only need to create an `async Task` method – Panagiotis Kanavos Sep 11 '20 at 14:02
  • If you used a proper pipeline of tasks and channels to read and process an infinite stream of messages, the correct design would be to *not* try to mix up different operations in the same step, especially if they produce different output like this code does. BTW that `State` dictionary isn't thread-safe, you should use `ConcurrentDictionary` instead. – Panagiotis Kanavos Sep 11 '20 at 14:05
  • 1
    It looks like you tried to rewrite a single-fire `select` in C#, even assuming the input is an `object` or should I say `interface{}` when it's strongly typed. This code won't even compile since `T` isn't a boolean. – Panagiotis Kanavos Sep 11 '20 at 14:08
  • @PanagiotisKanavos thanks for responding. You have a number of points I will try address starting with the first (1) Channels are traditional pub-sub, there's no in-memory list of subscribers to publish messages too. You can implement pub sub with it. (2) You are quite right, I will implement my socket stream here. (3) A channel manages concurrency and is thread safe hence it's use because all access to the channel is funneled through the channel. Am I wrong? (4) Correct, again contrived and I've resolved with a strong type. – Dane Balia Sep 15 '20 at 06:58

0 Answers0