В нашем приложении мы используем PngBitmapEncoder для кодирования и сохранения изображения PNG в отдельном потоке\задаче. После нескольких дней работы приложения мы видим, что Dispatcher не может быть создан из Encoder и выдает ошибку.
Недостаточно памяти для обработки команды
И имеет следующий стек вызовов
System.ComponentModel.Win32Exception (0x80004005): Not enough storage is available to process this command
at MS.Win32.HwndWrapper..ctor(Int32 classStyle, Int32 style, Int32 exStyle, Int32 x, Int32 y, Int32 width, Int32 height, String name, IntPtr parent, HwndWrapperHook[] hooks)
at System.Windows.Threading.Dispatcher..ctor()
at System.Windows.Threading.DispatcherObject..ctor()
at System.Windows.Media.Imaging.BitmapEncoder..ctor(Boolean isBuiltIn)
Поскольку .Net доступен с открытым исходным кодом, стало любопытно, какая строка внутри конструктора Dispatcher выдает ошибку.
[SecurityCritical, SecurityTreatAsSafe]
private Dispatcher()
{
_queue = new PriorityQueue<DispatcherOperation>();
_tlsDispatcher = this; // use TLS for ownership only
_dispatcherThread = Thread.CurrentThread;
// Add ourselves to the map of dispatchers to threads.
lock(_globalLock)
{
_dispatchers.Add(new WeakReference(this));
}
_unhandledExceptionEventArgs = new DispatcherUnhandledExceptionEventArgs(this);
_exceptionFilterEventArgs = new DispatcherUnhandledExceptionFilterEventArgs(this);
_defaultDispatcherSynchronizationContext = new DispatcherSynchronizationContext(this);
// Create the message-only window we use to receive messages
// that tell us to process the queue.
MessageOnlyHwndWrapper window = new MessageOnlyHwndWrapper();
_window = new SecurityCriticalData<MessageOnlyHwndWrapper>( window );
_hook = new HwndWrapperHook(WndProcHook);
_window.Value.AddHook(_hook);
// DDVSO:447590
// Verify that the accessibility switches are set prior to any major UI code running.
AccessibilitySwitches.VerifySwitches(this);
}
Обновлять
Обновлен код конструктора из .net с открытым исходным кодом. Dispatcher.cs доступен здесь https://referencesource.microsoft.com/#WindowsBase/Base/System/Windows/Threading/Dispatcher.cs,078d6b27d9837a35
После некоторого расследования мы обнаружили, что проблема возникает примерно после 15000 итераций (каждая итерация создает новый поток и вызывает PngBitmapEncoder). Затем выяснилось, что это связано с ограничением Global Atom Table (0x4000 или 16384). Подробнее о Global Atom Table здесь https://docs.microsoft.com/en-us/archive/blogs/ntdebugging/identifying-global-atom-table-leaks
Создаваемый каждый раз диспетчер вносит запись в таблицу Global Atom, и при выходе из потока эта запись не очищается. Это приводит к утечке в таблице Global Atom, и когда она достигает максимального предела, выдается ошибка «Недостаточно памяти....». Это похоже на проблему с обработкой Microsoft Dispatcher. Даже в документации PngBitmapEncoder я не вижу никаких замечаний относительно обработки Dispatcher и какого-либо явного отключения диспетчера.
.cctor
. Это не конструктор экземпляра, который выбрасывает. - person Jon Skeet   schedule 16.08.2020