通过引用传递给线程 C#?
Passing by reference to a thread C#?
我有一个方法,我打算 运行 在它自己的线程上,但不知道如何在设置线程时传递引用。
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 是否也是一种不好的做法。
这是possible/safe要做的吗?
您不需要 ref
关键字。 List<T>
是一个 class ,它是一个引用类型,所以它已经通过引用传递了。
嗯,准确地说,您传递的是引用,而且是按值传递的。如果您将引用分配给新的/不同的列表并希望将其传递回调用者,则只需要 ref
关键字。
如果您不打算更改列表,那么您最好传递 IEnumerable<T>
,因为它是只读的。List<T>
已经实现了 IEnumerable<T>
,因此您甚至不需要投射。
如果您从不同的线程访问列表,请注意它可能会随时更改(比如迭代到一半)。在这种情况下,您可能需要一个 ConcurrentList<T>
,它至少对 add/removes 是线程安全的。或者,如果你只是阅读,你可能会更好地通过在某个点上调用 ToArray()
或其他东西来创建列表的只读 "snapshot" 并传递它。
您不应在此处使用 ref
,而应使用可观察的集合,例如 ObservableCollection<T>
。 List<string>
已经是传递引用:)
先把clientDiscovery.OtherInstances
的类型改成ObservableCollection<string>
,然后把构造函数的参数类型也改成ObservableCollection<string>
。删除所有 ref
,您不需要这些。
现在,将 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;
请注意,这不会处理列表中的初始项(只会添加后续更改),因此您可能希望在上一行之后直接处理初始项。
我有一个方法,我打算 运行 在它自己的线程上,但不知道如何在设置线程时传递引用。
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 是否也是一种不好的做法。
这是possible/safe要做的吗?
您不需要 ref
关键字。 List<T>
是一个 class ,它是一个引用类型,所以它已经通过引用传递了。
嗯,准确地说,您传递的是引用,而且是按值传递的。如果您将引用分配给新的/不同的列表并希望将其传递回调用者,则只需要 ref
关键字。
如果您不打算更改列表,那么您最好传递 IEnumerable<T>
,因为它是只读的。List<T>
已经实现了 IEnumerable<T>
,因此您甚至不需要投射。
如果您从不同的线程访问列表,请注意它可能会随时更改(比如迭代到一半)。在这种情况下,您可能需要一个 ConcurrentList<T>
,它至少对 add/removes 是线程安全的。或者,如果你只是阅读,你可能会更好地通过在某个点上调用 ToArray()
或其他东西来创建列表的只读 "snapshot" 并传递它。
您不应在此处使用 ref
,而应使用可观察的集合,例如 ObservableCollection<T>
。 List<string>
已经是传递引用:)
先把clientDiscovery.OtherInstances
的类型改成ObservableCollection<string>
,然后把构造函数的参数类型也改成ObservableCollection<string>
。删除所有 ref
,您不需要这些。
现在,将 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;
请注意,这不会处理列表中的初始项(只会添加后续更改),因此您可能希望在上一行之后直接处理初始项。