У нас есть управляемое приложение .Net/С#, которое создает задачи TPL для выполнения кодирования метаданных JPEG на изображениях JPEG. Каждая задача строится с параметром TaskCreationOptions.LongRunning, например,
Task task = new Task( () => TaskProc(), cancelToken, TaskCreationOptions.LongRunning );
TaskProc() использует классы JpegBitmapDecoder и JpegBitmapEncoder для добавления метаданных JPEG и сохранения новых изображений на диск. Мы позволяем до двух таких задач быть активными в любой момент времени, и этот процесс должен продолжаться бесконечно.
После некоторого времени выполнения вышеупомянутого мы получаем Недостаточно свободного места для хранения для обработки этой исключительной команды при попытке создать экземпляр класса JpegBitmapDecoder:
System.ComponentModel.Win32Exception (0x80004005): недостаточно памяти доступно для обработки этой команды в MS.Win32.UnsafeNativeMethods.RegisterClassEx(WNDCLASSEX_D wc_d)
в MS.Win32.HwndWrapper..ctor(Int32 classStyle, Int32 style, Int32 exStyle, Int3 2 x, Int32 y, Int32 width, Int32 height, String name, Родитель IntPtr, HwndWrapperHoo k [] hooks) в System.Windows.Threading.Dispatcher..ctor() at System.Windows.Threading.Dispatcher.get_CurrentDispatcher() в System.Windows.Media.Imaging.BitmapDecoder..ctor(Stream bitmapStream, BitmapC reateOptions createOptions, BitmapCacheOption cacheOption, Guid expectedClsId) в System.Windows.Media.Imaging.JpegBitmapDecoder..ctor(Stream bitmapStream, бит mapCreateOptions createOptions, BitmapCacheOption cacheOption)
Произошла ошибка только, когда мы использовали JpegBitmapDecoder для добавления метаданных. Другими словами, если задача просто закодирует и сохранит изображение Bitmap в файл, никаких проблем не возникнет. Ничего очевидного не было обнаружено при использовании Process Explorer, Process Monitor или других диагностических инструментов. Никакой поток, память или утечка ручек не наблюдались вообще. При возникновении такой ошибки новые приложения не могут быть запущены, например, блокнот, слово и т.д. Как только наше приложение будет прекращено, все вернется к нормальному состоянию.
Опция создания задачи LongRunning определена в MSDN как Указывает, что задача будет длительной, крупнозернистой. Он дает подсказку TaskScheduler о том, что избыточная подписка может быть оправдана. Это означает, что поток, выбранный для запуска задачи, может быть не из ThreadPool, т.е. Он будет создан для цели задачи. Другие параметры создания задачи приведут к тому, что для задачи будет выбран поток ThreadPool.
После некоторого анализа времени и тестирования мы изменили способ создания задачи на что угодно, кроме LongRunning, например, PreferFairness. Никаких других изменений кода не было сделано вообще. Это "разрешило" проблему, то есть больше не закончилось ошибок в хранилище.
Мы озадачены фактической причиной того, что потоки LongRunning являются виновниками. Вот некоторые из наших вопросов по этому поводу:
-
Почему должен произойти тот факт, что потоки, выбранные для выполнения задачи, происходят из ThreadPool или нет? Если поток завершается, не должны ли его ресурсы восстанавливаться со временем GC и возвращаться обратно в ОС, независимо от его происхождения?
-
Что особенно важно в сочетании задачи LongRunning и функции JpegBitmapDecoder, которая вызывает ошибку?