Есть немного кода, который записывает данные в объект MemoryStream непосредственно в его буфер данных, вызывая GetBuffer (). Он также соответствующим образом использует и обновляет свойства Position и SetLength ().
Этот код работает правильно 99,9999% времени. В прямом смысле. Только через каждые 100 000 повторений это будет срывом. Конкретная проблема заключается в том, что свойство Position MemoryStream неожиданно возвращает ноль вместо соответствующего значения.
Однако был добавлен код, который проверяет 0 и генерирует исключение, которое включает журнал свойств MemoryStream, таких как Position и Length, в отдельном методе. Те возвращают правильное значение. Дальнейшее добавление регистрации в том же методе показывает, что когда возникает это редкое условие, позиция имеет только ноль внутри этого конкретного метода.
Хорошо. Очевидно, это должно быть проблемой с многопоточностью. И, скорее всего, проблема оптимизации компилятора.
Однако природа этого программного обеспечения состоит в том, что оно организовано «задачами» с планировщиком, и поэтому любой из нескольких реальных потоков O / S может запускать этот код в любой момент времени, но никогда не более, чем по одному за раз. .
Так что я предполагаю, что обычно случается так, что один и тот же поток продолжает использоваться для этого метода, а затем в редких случаях используется другой поток. (Просто напишите идею для проверки этой теории, захватив и сравнив идентификатор потока.)
Тогда из-за оптимизации компилятора другой поток никогда не получит правильное значение. Он получает «устаревшее» значение.
Обычно в такой ситуации я бы применял ключевое слово «volatile» к рассматриваемой переменной, чтобы посмотреть, исправит ли она это. Но в этом случае переменные находятся внутри объекта MemoryStream.
У кого-нибудь есть другая идея? Или это означает, что мы должны реализовать наш собственный объект MemoryStream?
С уважением,
Wayne
EDIT: просто запустил тест, который подсчитывает общее количество вызовов этого метода и подсчитывает, сколько раз ManagedThreadId отличается от последнего вызова. Почти точно 50% времени он переключает потоки - чередуя их. Так что моя теория, приведенная выше, почти наверняка неверна, иначе ошибка будет встречаться гораздо чаще.
РЕДАКТИРОВАТЬ: эта ошибка возникает настолько редко, что для запуска без ошибки потребуется почти неделя, прежде чем почувствовать какую-либо уверенность, что она действительно исчезла. Вместо этого лучше проводить эксперименты, чтобы точно определить природу проблемы.
РЕДАКТИРОВАТЬ: Блокировка в настоящее время обрабатывается с помощью операторов lock () в каждом из 5 методов, которые используют MemoryStream.