2010-07-10 36 views
0

我有一个List和两个使用List的线程。使用相同变量的两个线程产生问题

第一个线程正在获取新连接,每个新连接都被添加到列表中。

第二个线程遍历List以处理连接(使用foreach)。

问题是,有时在第二个线程在List上循环时,List在循环结束前发生更改。我甚至尝试创建列表的新副本并循环播放。但它会产生一些其他问题。

我不想创建一个新的线程来处理每个新的连接,因为我知道太多的线程会损害性能。有没有其他的方式来处理连接?

+3

如果你正在学习如何做线程,不要相信其他人,当他们说“太多的线程可能会损害性能”。自己动手,找出在你的情况下真正的限制。你将会学到更多的东西。 – 2010-07-10 12:30:08

+0

复制列表会出现什么样的问题? – SwDevMan81 2010-07-10 12:35:25

+0

它表示目标阵列大小不正确(在复制完成之前阵列发生更改)。 – 2010-07-10 13:03:23

回答

3

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根据您的喜好排列尽可能多的请求,并且框架将负责将它们映射到实际线程上,而不会中止系统。

+0

'BlockingCollection'只能将*修改*同步到列表中。迭代仍然需要明确的锁定。 – 2010-07-10 12:42:07

+0

是的,你是对的,枚举必须被淘汰,可以使用'Take()'来代替。 – Mau 2010-07-10 12:46:10

-4

我真的不知道很多关于这一点,但来到我脑海的第一个念头是:

商店列表的长度,使循环去

while (var i < lengthoflist) 
{ 
//whatever fancy stuff your code does 
i++; 
} 

但我知道没有任何关于套接字,这只是我想到的一个通用想法,所以我不知道这是否会起作用。

+0

它不会工作(安全)。它可能不会抛出,但它可能会跳过项目或类似的东西。 – 2010-07-10 12:34:31

1

最简单的解决方案是在每个线程的列表中使用lock

第一线:

lock(yourList) 
{ 
    yourList.Add(...); 
} 

第二个线程:

lock(yourList) 
{ 
    foreach(var item in yourList) 
    { 
     ... 
    } 
} 

这将阻止第一个线程被添加一个连接,而第二个线程在遍历它的中间。如果第二个线程遍历列表并且第一个线程尝试输入代码段,它将等到第二个线程完成列表循环。

+0

@Jouke:是的,这就是我在答案最后所说的。这听起来像OP所要求的。 – 2010-07-10 12:36:24

1

正如其他人写的,你需要使用锁来管理这两个线程的并发性。

至于避免多个线程,我认为这在很大程度上取决于我们正在讨论的线程数。如果你只使用几个套接字连接,那么也许在它自己的线程中进行同步读取是可以的。但是如果你正在谈论很多套接字连接,那么我不会为每个连接使用一个专用线程。

到目前为止,从多个套接字读取最有效的方法是使用异步读取(Socket.BeginReceive())。在引擎盖下,这些异步方法使用I/O Completion Ports,这是非常高效的。实现可以使用异步Socket方法处理数千个并发连接的自定义TCP服务器相对容易。

相关问题