2010-12-13 2 views
1

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

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

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

+3

Немного наблюдений, не связанных с OOM. 1. Вы сериализуете доступ ко всему коду в DoWork, поэтому нет смысла использовать несколько потоков. 2. Вы не используете переданное значение stateInfo. 3. Вы получаете доступ к MyList за пределами блокировки, поэтому этот доступ не является потокобезопасным. –

ответ

5

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

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

Лучшим вариантом было бы использовать счетчик, который вы увеличиваете с помощью 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.."); 
     } 
    } 
+0

Возможно, это правильный ответ. Для OP также обратите внимание на то, что @chibacity говорит и в его комментарии. Нет никакой пользы для многопоточности, как вы это делаете. Возможно, комментарий должен быть перенесен в ответ, а также для большей наглядности. – Davy8

+0

Я удивлен, что никто не дал этому +1. – Davy8

+0

Спасибо Дэви, но я получаю сообщение об ошибке (index = Interlocked.Increment (ref _counter) zSynopsis

Смежные вопросы