【问题标题】:Two threads using the same variable, produces a problem两个线程使用相同的变量,会产生问题
【发布时间】:2010-07-10 12:24:32
【问题描述】:

我有一个列表和两个使用该列表的线程。

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

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

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

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

【问题讨论】:

  • 如果您正在学习如何进行线程处理,请不要相信其他人说“线程过多会影响性能”。自己做,找出你的情况到底有什么限制。这样你会学到更多。
  • 复制列表会出现什么样的问题?
  • 表示目标数组大小不合适(复制完成前数组发生变化)。

标签: c# multithreading sockets c#-4.0


【解决方案1】:

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 将任意数量的请求排入队列,框架会负责将它们映射到实际线程上,而不会杀死系统。

【讨论】:

  • BlockingCollection 仅将修改同步到列表。迭代仍然需要显式锁定。
  • 是的,你是对的,枚举必须被消除,可以使用Take()来代替。
【解决方案2】:

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

第一个线程:

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

第二个线程:

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

这将防止第一个线程在第二个线程正在对其进行迭代时添加连接。如果第二个线程正在遍历列表并且第一个尝试进入lock-ed 代码段,它将等待直到第二个线程完成对列表的循环。

【讨论】:

  • @Jouke:是的,这就是我在答案末尾所说的。这听起来像是 OP 要求的。
【解决方案3】:

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

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

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

【讨论】:

    【解决方案4】:

    我真的不太了解这个,但是我想到的第一个想法是:

    存储列表的长度并进行循环

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

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

    【讨论】:

    • 它不会(安全地)工作。它可能不会抛出,但它可能会跳过项目或类似的东西。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-08-14
    • 1970-01-01
    • 2012-03-06
    • 2017-06-28
    • 2012-07-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多