我有一个List和两个使用List的线程。使用相同变量的两个线程产生问题
第一个线程正在获取新连接,每个新连接都被添加到列表中。
第二个线程遍历List以处理连接(使用foreach)。
问题是,有时在第二个线程在List上循环时,List在循环结束前发生更改。我甚至尝试创建列表的新副本并循环播放。但它会产生一些其他问题。
我不想创建一个新的线程来处理每个新的连接,因为我知道太多的线程会损害性能。有没有其他的方式来处理连接?
我有一个List和两个使用List的线程。使用相同变量的两个线程产生问题
第一个线程正在获取新连接,每个新连接都被添加到列表中。
第二个线程遍历List以处理连接(使用foreach)。
问题是,有时在第二个线程在List上循环时,List在循环结束前发生更改。我甚至尝试创建列表的新副本并循环播放。但它会产生一些其他问题。
我不想创建一个新的线程来处理每个新的连接,因为我知道太多的线程会损害性能。有没有其他的方式来处理连接?
2个问题。
1)您需要锁定列表。 您需要确保您拥有对列表的互斥访问权限,因此在修改时无法枚举。 第一种解决方案是使用锁:
class Mailbox {
List<int> list;
void Send(int a) {
lock(list) {
list.Add(a);
}
}
int Receive() {
lock(list) {
// Enumerate
return ...;
}
}
}
更优雅,你可以使用在Concurrent命名空间中的新的收藏品之一,就像BlockingCollection。后者不是枚举安全的,但提供了一个可用于从中检索对象的方法,而制作者正在插入它们。
2)避免创建gazillion线程。 您可以使用.NET thread pool根据您的喜好排列尽可能多的请求,并且框架将负责将它们映射到实际线程上,而不会中止系统。
'BlockingCollection'只能将*修改*同步到列表中。迭代仍然需要明确的锁定。 – 2010-07-10 12:42:07
是的,你是对的,枚举必须被淘汰,可以使用'Take()'来代替。 – Mau 2010-07-10 12:46:10
我真的不知道很多关于这一点,但来到我脑海的第一个念头是:
商店列表的长度,使循环去
while (var i < lengthoflist)
{
//whatever fancy stuff your code does
i++;
}
但我知道没有任何关于套接字,这只是我想到的一个通用想法,所以我不知道这是否会起作用。
它不会工作(安全)。它可能不会抛出,但它可能会跳过项目或类似的东西。 – 2010-07-10 12:34:31
最简单的解决方案是在每个线程的列表中使用lock
。
第一线:
lock(yourList)
{
yourList.Add(...);
}
第二个线程:
lock(yourList)
{
foreach(var item in yourList)
{
...
}
}
这将阻止第一个线程被添加一个连接,而第二个线程在遍历它的中间。如果第二个线程遍历列表并且第一个线程尝试输入代码段,它将等到第二个线程完成列表循环。
@Jouke:是的,这就是我在答案最后所说的。这听起来像OP所要求的。 – 2010-07-10 12:36:24
正如其他人写的,你需要使用锁来管理这两个线程的并发性。
至于避免多个线程,我认为这在很大程度上取决于我们正在讨论的线程数。如果你只使用几个套接字连接,那么也许在它自己的线程中进行同步读取是可以的。但是如果你正在谈论很多套接字连接,那么我不会为每个连接使用一个专用线程。
到目前为止,从多个套接字读取最有效的方法是使用异步读取(Socket.BeginReceive())。在引擎盖下,这些异步方法使用I/O Completion Ports,这是非常高效的。实现可以使用异步Socket方法处理数千个并发连接的自定义TCP服务器相对容易。
如果你正在学习如何做线程,不要相信其他人,当他们说“太多的线程可能会损害性能”。自己动手,找出在你的情况下真正的限制。你将会学到更多的东西。 – 2010-07-10 12:30:08
复制列表会出现什么样的问题? – SwDevMan81 2010-07-10 12:35:25
它表示目标阵列大小不正确(在复制完成之前阵列发生更改)。 – 2010-07-10 13:03:23