【问题标题】:Passing by reference to a thread C#?通过引用传递给线程 C#?
【发布时间】:2020-05-03 04:10:25
【问题描述】:

我有一个方法,我打算在它自己的线程上运行,但在设置线程时不知道如何传递引用。

 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--;
                }
            }
        }

期望的行为是,当原始列表(instanceAddresses)更改时,此方法可以开始工作,并且可以设置新客户端并将其添加到另一个列表中。

这是调用线程启动的方法:

 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
        }

这是在哪里构造的:

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

我也不确定这是否也是通过不同类传递 ref 的不好做法。

这可能/安全吗?

【问题讨论】:

  • 这不是你应该使用传递引用的地方。您应该使用可观察的集合。
  • @Sweeper 谢谢,不知道这是一回事。但是,我将这个列表作为构造函数传递给这个类,因为它来自其他地方。同样,不确定这是否是不好的做法。
  • 能否也显示方法的调用方?
  • 那您打算在//Thread would be setup here 线上拨打ManageCollections 吗?您还没有显示instanceAddresses 的来源。基本上,无论它来自哪里,都将其更改为ObservableCollection&lt;string&gt;。
  • @Sweeper 抱歉,不清楚,是的,那里会调用 ManageCollections。 instanceAddreses 是在构造函数中传递的引用(参见底部的 sn-p)。它来自另一个类(clientDiscovery)

标签: c# multithreading reference ref


【解决方案1】:

此处不应使用ref,而应使用可观察的集合,例如ObservableCollection&lt;T&gt;。 List&lt;string&gt; 已经是传递引用:)

首先将clientDiscovery.OtherInstances的类型改为ObservableCollection&lt;string&gt;,然后将构造函数的参数类型也改为ObservableCollection&lt;string&gt;。删除所有refs,你不需要那些。

现在,将ManageConnections 重写为这个签名(你需要using System.Collections.Specialized):

private void ManageConnections(object sender, NotifyCollectionChangedEventArgs e) {

}

在这里,您将检查 e.NewItems 以查看哪些项目已添加到 instanceAddresses 列表中,并将它们中的每一个添加到另一个列表中:

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

如果还有已删除的项目,您可能想要做一些事情。如果你想处理这个问题,请使用e.OldItems。这些是删除的项目。

现在,您无需调用ManageConnections,而是:

instancesToAdd.CollectionChanged += ManageConnections;

请注意,这不会处理列表中的初始项(只会添加后续更改),因此您可能希望直接在上述行之后处理初始项。

【讨论】:

  • 这看起来很棒,感谢您的帮助!我正在处理其他地方的初始项目,所以这看起来完全符合我的需要!
【解决方案2】:

您不需要ref 关键字。 List&lt;T&gt; 是一个引用类型的类,所以它已经通过引用传递了。

嗯,准确地说,您是在传递引用,而那是按值传递的。如果您将引用分配给新的/不同的列表并希望将其传递回调用者,则只需要 ref 关键字。

如果您不打算更改列表,那么您最好传递IEnumerable&lt;T&gt;,因为它是只读的。List&lt;T&gt; 已经实现了IEnumerable&lt;T&gt;,因此您甚至不需要强制转换。

如果您从不同的线程访问列表,请注意它可能随时更改(例如在迭代过程中途)。在这种情况下,您可能需要一个ConcurrentList&lt;T&gt;,它至少对于添加/删除是线程安全的。或者,如果您只是在阅读,您最好在某个时间点创建列表的只读“快照”,方法是在其上调用 ToArray() 或其他东西并传递它。

【讨论】:

  • 太棒了,感谢您的意见。是的,列表只是在它创建的类中被改变,只是在其他地方被阅读。如果此列表要更改,如果我理解正确,它是否也会在新线程中更新?
  • 正确。这是同一个列表。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-08-12
  • 1970-01-01
  • 2013-06-17
  • 1970-01-01
  • 1970-01-01
  • 2018-05-28
  • 2012-06-25
相关资源
最近更新 更多