Нулевая ссылка в асинхронном цикле, но объект не имеет нулевых значений - PullRequest
0 голосов
/ 10 ноября 2019

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

У меня есть многопоточное приложение .NET winforms, и я делаю это:

if ( paramList != null ) {
   lock ( paramList ) {
      foreach ( DictionaryEntry param in paramList ) {
         command.Parameters.AddWithValue(param.Key.ToString(), param.Value);
      }
   }
}

paramList - это OrderedDictionary.

. Я периодически получаю эту ошибку в строке foreach:

Ссылка на объект не установлена ​​на экземпляр объекта.

enter image description here

Как видите, param.Key равно нулю, а param.Value равно нулю. Но это не имеет смысла, потому что в paramList нет нулей, как вы можете видеть здесь:

enter image description here

На скриншоте вы можете видеть только индекс2, но я также исследовал индексы 0 и 1, то же самое, действительные данные, без нулей.

У меня нет опыта работы с многопоточными приложениями, но я поместил этот блок в lock() из-за ответов в это ТАК сообщение . Перед установкой lock() я время от времени получал ошибку Collection was modified; enumeration operation may not execute. После установки блокировки эта ошибка исчезла, но теперь я получаю ссылку на объект, как показано выше.

Что я могу сделатьЧтобы решить эту проблему?

РЕДАКТИРОВАТЬ

Воспользовавшись советом нескольких плакатов, я сделал это:

private static object syncLock = new object();

и затем в использовании:

lock ( syncLock ) {
   if ( paramList != null ) {
       foreach ( DictionaryEntry param in paramList ) {
          command.Parameters.AddWithValue(param.Key.ToString(), param.Value);
       }
   }
}

Это, похоже, решило ошибку ссылки на объект (спасибо всем), но теперь я время от времени получаю:

Коллекция была изменена;Операция перечисления может не выполняться.

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

Все еще ищу решение, если у кого-то есть идеи.

Ответы [ 3 ]

1 голос
/ 10 ноября 2019

(предпочтительно) Измените paramList, чтобы вам не нужно было его блокировать.

Если вам нужно поделиться, то:

  1. Блокировка объекта, который не изменяется, например, private readonly object l = new object(); или private static readonly, если необходимо.
  2. Убедитесь, что код, который заполняет paramList, также lock ed.
0 голосов
/ 12 ноября 2019

Это решение, которое решило все мои проблемы:

var connection = getConnectionFromPool(sourceOrDC);

try {
   lock ( connection ) {
      using ( SqlCommand command = new SqlCommand(sql, connection) ) {
         command.CommandType = CommandType.Text;

         if ( paramList != null ) {
            foreach ( DictionaryEntry param in paramList ) {
               command.Parameters.AddWithValue(param.Key.ToString(), param.Value);
            }
         }

         command.CommandTimeout = 0;
         command.ExecuteNonQuery();
      }
   }

   return true;
}

Мне пришлось заключить больше кода в lock() и заблокировать соединение.

0 голосов
/ 10 ноября 2019

Ваш код не будет работать. Что если он не был нулевым во время проверки на ноль, а нулевым, когда вы приходите к замку?

Постоянно рекомендуется всегда иметь специальную вещь для блокировки на . object _mutex = new object(); стало привычным.

Редактировать: подразумевается частное, но вы, вероятно, хотите сделать его доступным только для чтения, как предложил Чарльз.

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

Пример кода:

//_mutex is private, readonly and exist exclusively for this operation
lock(_mutex){
  //You only do null checks after you got a lock
  if ( paramList != null ){
    foreach ( DictionaryEntry param in paramList ) {
      command.Parameters.AddWithValue(param.Key.ToString(), param.Value);
    }
  }
}

Весь другой код читает, записывает или обрабатывает переменную paramList, любой из его словаря. все и их поля - включая любые копии * - также должны быть в lock(_mutex). Таким образом, они не могут сталкиваться друг с другом.

* копии примитивных типов и строк являются исключением. Встроенные примитивные типы неизменны. Также обычно не используйте эталонную механику. И класс String также стал неизменным, чтобы он мог работать таким образом

...