другой поток обращается к MemoryStream - PullRequest
6 голосов
/ 13 мая 2010

Есть немного кода, который записывает данные в объект 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.

1 Ответ

5 голосов
/ 13 мая 2010

(действительно нужен примерный код для подтверждения.)

MemoryStream элементы не задокументированы как поточно-ориентированные (например, Position), поэтому вам необходимо убедиться, что вы имеете доступ только к этому экземпляру (или к любой ссылке на объект логически часть MemoryStream) из одного потока за раз.

Но MemoryStream - это , а не , задокументированный как имеющий привязку к потоку, поэтому вы можете получить доступ к экземпляру из другого потока & mdash; при условии, что такой доступ не является одновременным.

Резьба трудная (аксиоматично для этих вопросов и ответов).

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

Я бы позаботился о том, чтобы сохранить блокировку как можно более простой (попытка быть очень умной, а ограничение блокировки часто является причиной очень трудных для поиска ошибок) и заставить все работать. Тестирование на многоядерной системе также может помочь. Попытайтесь оптимизировать блокировку только в том случае, если профилирование показывает, что существует потенциал для значительного чистого выигрыша (приложения в целом).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...