使用相同变量的两个线程产生问题

时间:2010-07-10 12:24:32

标签: c# multithreading sockets c#-4.0

我有一个List和两个使用List的线程。

第一个线程是获取新连接,每个新连接都添加到List。

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

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

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

4 个答案:

答案 0 :(得分: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。后者不是枚举安全的,但是提供了一个Take()方法,可以用来从中检索对象,而生产者插入它们。

2)避免创建大量的线程。 您可以使用.NET thread pool将任意数量的请求排入队列,框架将负责将它们映射到实际线程,而不会杀死系统。

答案 1 :(得分:1)

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

第一个帖子:

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

第二个帖子:

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

这将阻止第一个线程在第二个线程处于迭代过程中时添加连接。如果第二个线程正在遍历列表并且第一个线程尝试输入代码的lock - ed部分,它将等到第二个线程完成循环遍历列表。

答案 2 :(得分:1)

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

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

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

答案 3 :(得分:-4)

我对此并不太了解,但我想到的第一个想法是:

存储列表的长度并循环播放

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

但我对套接字一无所知,这只是我想到的一个通用的想法,所以我不知道这是否会奏效。