【问题标题】:Threadpool out of memory exception线程池内存不足异常
【发布时间】:2010-12-14 06:35:54
【问题描述】:

我不断收到以下代码的内存不足异常,我想知道是否可以采取一些措施来阻止这种情况的发生。

  private static List<string> MyIds { get; set; }
  private static object LockObject { get; set; }
  private static int Counter { get; set; }
  private static readonly NumOfThreads = 5;

  static void Main(string[] args)
  {
      try
      {
          Console.Clear();
          LockObject = new object();
          // Pull id's into memory (A list of around 1 million ids)
          MyIds = _repository.GetIds();
          for (int i = 0; i < NumOfThreads ; i++)
                ThreadPool.QueueUserWorkItem(new WaitCallback(DoWork), (object)i);

       }
       catch (Exception ex)
       {
           Console.WriteLine(ex.StackTrace);
       }
   }

   public static void DoWork(Object stateInfo)
   {
        while (MyList.Count > 0)
        {
            lock (LockObject)
            {
                if (MyList.Count == 0)
              return;

             string id = MyList[0];

          var record = _repository.GetRecord(id);
             _repository.Add(record);

          Counter++;
          if (Counter % 100 == 0)
                            System.Console.WriteLine(DateTime.Now + " - Imported " + Counter.ToString() + " Records..");

                MyList.RemoveAt(0);
            }
        }
    }

感谢您的帮助

【问题讨论】:

  • 很少有与 OOM 无关的观察结果。 1. 您正在序列化对 DoWork 中所有代码的访问,因此使用多个线程毫无意义。 2.您没有使用传入的 stateInfo 值。 3. 您在锁之外访问 MyList,因此这种访问不是线程安全的。

标签: c# .net threadpool out-of-memory


【解决方案1】:

您正在从列表的开头删除,这将导致生成一个新列表并将旧列表复制到其中。鉴于您正在处理包含大量元素的列表,这将对Large Object Heap 造成严重破坏。

如果您必须使用这种类型的设计来反向删除项目,这将防止复制 List 的底层数组,即从末尾向开头删除。

更好的设计是使用您使用 Interlocked.Increment 递增的计数器,并使用它来访问列表中的成员。您可以安全地执行此操作,因为您在创建列表后不会更改它。

更新

来自我的评论

您正在序列化对 DoWork 中所有代码的访问,因此使用多个线程毫无意义。

以下内容将避免从 ID 列表中删除的问题,此外还允许您从存储库中同时检索项目,从而利用这些额外的线程。不过,这对我来说必须衡量 - 添加线程并不能保证性能提高。

此外,如果您的“_repository”是一个集合,请确保将其调整为与您的 ID 列表大小相同的大小。这将防止在您添加项目时随着集合的增长而进行大量中间数组复制。

    private static int _counter = -1;

    public static void DoWork(Object stateInfo)
    {
        int index;

        while ((index = Interlocked.Increment(ref _counter)) < MyList.Count)
        {
            string id = MyList[index];

            var record = _repository.GetRecord(id);

            lock (LockObject)
            {                    
                _repository.Add(record);
            }

            if (index % 100 == 0)
                Console.WriteLine(DateTime.Now + " - Imported " + (index + 1) + " Records..");
        }
    }

【讨论】:

  • 这可能是正确的答案。对于 OP,还要注意 @chibacity 在他的评论中所说的话。以您的方式进行多线程处理确实没有任何好处。也许评论也应该移到答案中以获得更多可见性。
  • 我很惊讶没有其他人对此给予 +1。
  • 谢谢戴维,但我在 while (index = Interlocked.Increment(ref _counter)
  • @zSysop 糟糕,我用 SO 编写了代码。我已经更正了 while 子句并检查了它是否可以编译 - 缺少括号...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-05
  • 1970-01-01
相关资源
最近更新 更多