Исключение из пула нехватки памяти - PullRequest
1 голос
/ 14 декабря 2010

Я продолжаю получать исключение нехватки памяти для следующего кода, и мне было интересно, могу ли я что-нибудь сделать, чтобы это не произошло.

  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);
            }
        }
    }

Спасибо за любую помощь

1 Ответ

5 голосов
/ 14 декабря 2010

Вы удаляете из начала списка, что приведет к созданию нового списка и копированию в него старого.Это приведет к хаосу с кучей больших объектов , учитывая, что вы имеете дело со списками с большим количеством элементов.

Если вы должны использовать этот тип дизайна, удаляйте элементы в обратном направлении,это предотвратит копирование базового массива списка, т. е. удаления от конца к началу.

Лучше было бы использовать счетчик, который вы увеличиваете с помощью Interlocked.Increment, и использовать его для доступа к членам вашегосписок.Вы можете сделать это безопасно, поскольку вы не меняете свой список после того, как создали его.

Обновлено

Из моего комментария

Высериализует доступ ко всему коду в DoWork, поэтому нет смысла использовать несколько потоков.

Что-то вроде следующего позволит избежать проблемы с удалением из вашего списка идентификаторов, а также позволит вам потенциально получать элементы одновременно изваш репозиторий и так использовать эти дополнительные темы.Хотя это нужно было бы измерить - добавление потоков не является гарантией повышения производительности.

Также, если ваш «_repository» является коллекцией, убедитесь, что его размер равен размеру списка идентификаторов.Это предотвратит большое копирование промежуточного массива по мере увеличения коллекции при добавлении элементов.

    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..");
        }
    }
...