简体   繁体   中英

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?

You do not need the ref keyword. List<T> is a class which is a reference type, so it's already passed by reference.

Well, to be precise you're passing the reference around, and THAT is passed by value. You would only need the ref keyword if you assigned the reference to a new / different list and wanted that passed back to the caller.

If you do not intend to alter the list, then you're probably better passing IEnumerable<T> instead, since that is read only . List<T> already implements IEnumerable<T> , so you don't even need to cast.

If you're accessing the list from different threads, then be aware it could change AT ANY TIME (like half way through iterating it). In this case you may want a ConcurrentList<T> which is at least thread safe for add/removes. Alternatively if you're only reading, you may be better to create a readonly "snapshot" of the list at a certain point by calling ToArray() on it or something and passing that around instead.

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 ref s, 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM