Search code examples
c#multithreadingreferenceref

Passing by reference to a thread C#?


I have a method that I intend to run on its own thread, but can't figure out how to pass the reference when setting up the thread.

 private void ManageConnections(ref List<string> instanceAddresses) 
        {
            int connected = Instances.Count();

            if(instanceAddresses.Count() > connected)
            {
                int instancesToAdd = instanceAddresses.Count() - connected;

                while(instancesToAdd != 0)
                {
                    Channel channel = new Channel(instanceAddresses[instanceAddresses.Count - instancesToAdd], ChannelCredentials.Insecure);
                    var client = new ConfigurationDirectoryService.ConfigurationDirectoryServiceClient(channel);
                    Instances.Add(client);
                    instancesToAdd--;
                }
            }
        }

Desired behaviour is that when the original list (instanceAddresses) is changed, this method can get to work and can set up a new client and add it to another list.

This is the method that would call the thread start:

 public CDS_Service(ref List<string> instanceAddresses)
        {
            Thread manageAvaliable = new Thread(CheckAvaliability);
            manageAvaliable.Start();

            if(instanceAddresses.Count() > 0)
            {
                foreach(string instanceAddr in instanceAddresses)
                {
                    Channel channel = new Channel(instanceAddr, ChannelCredentials.Insecure);
                    var client = new ConfigurationDirectoryService.ConfigurationDirectoryServiceClient(channel);
                    Instances.Add(client);
                }

                foreach(CM commandManager in Instances[0].SyncDirectory(new Empty { }).CommandManagers)
                {
                    List<string> commands = new List<string>();

                    foreach(string command in commandManager.Commands)
                    {
                        commands.Add(command);
                    }

                    Directory.Add(new CommandManager(commandManager.Address, commands, commandManager.IsActive));
                }
            }

//Thread would be setup here
        }

And where this is constructed:

Server server = new Server
            {
                Services = { ConfigurationDirectoryService.BindService(new CDS_Service(ref clientDiscovery.OtherInstances)) },
                Ports = { new ServerPort(addr, PORT, ServerCredentials.Insecure) }
            };

I'm also not sure if this is also bad practice passing a ref around through different classes like this.

Is this possible/safe to do?


Solution

  • Instead of using ref here, you should use an observable collection, like ObservableCollection<T>. List<string> is already pass-by-reference :)

    First, change the type of clientDiscovery.OtherInstances to ObservableCollection<string>, then change the parameter type of the constructor to ObservableCollection<string> as well. Remove all the refs, you don't need those.

    Now, rewrite ManageConnections to this signature (You'll need using System.Collections.Specialized):

    private void ManageConnections(object sender, NotifyCollectionChangedEventArgs e) {
    
    }
    

    Here, you will check e.NewItems to see which items have been added to the instanceAddresses list, and add each of them to another list:

    foreach (var item in e.NewItems) {
        Channel channel = new Channel(item, ChannelCredentials.Insecure);
        var client = new ConfigurationDirectoryService.ConfigurationDirectoryServiceClient(channel);
        Instances.Add(client);
    }
    

    You might want to do something if the there are removed items as well. If you want to handle that, use e.OldItems. Those are the removed items.

    Now, instead of calling ManageConnections, you do:

    instancesToAdd.CollectionChanged += ManageConnections;
    

    Note that this won't handle the initial items in the list (only subsequent changes will be added), so you might want to handle the initial items straight after the line above.