Invoke действует как пара BeginInvoke / EndInvoke в том смысле, что он отправляет сообщение в поток пользовательского интерфейса, создает дескриптор и ожидает на этом дескрипторе, чтобы определить, когда завершен вызываемый метод. Именно эта ручка "протекает". Вы можете видеть, что это безымянные события, использующие Process Explorer для отслеживания дескрипторов во время работы приложения.
Если бы IASyncResult был IDisposable, утилизация объекта позаботилась бы о очистке ручки. Поскольку это не так, дескрипторы очищаются, когда сборщик мусора запускается и вызывает финализатор объекта IASyncResult. В этом можно убедиться, добавив GC.Collect () после каждых 20 вызовов DoStuff - число дескрипторов падает каждые 20 секунд. Конечно, «решение» проблемы путем добавления вызовов в GC.Collect () является неправильным способом решения проблемы; пусть сборщик мусора сделает свое дело.
Если вам не нужно , чтобы вызов Invoke был синхронным, используйте BeginInvoke вместо Invoke и не вызывайте EndInvoke; конечный результат будет делать то же самое, но никакие дескрипторы не будут созданы или «пропущены».