Я думаю, что необходимо начать их в C. Чем раньше они могут приобрести навык указателей, тем лучше.
Я немного побегал test для вызова делегата Action и выдачи в нем исключения. Затем я убеждаюсь, что не затопляю пул потоков, поддерживая только указанное количество потоков, выполняющихся одновременно, и постоянно заполняю пул потоков после завершения вызова удаления. Вот код:
static void Main(string[] args)
{
const int width = 2;
int currentWidth = 0;
int totalCalls = 0;
Action acc = () =>
{
try
{
Interlocked.Increment(ref totalCalls);
Interlocked.Increment(ref currentWidth);
throw new InvalidCastException("test Exception");
}
finally
{
Interlocked.Decrement(ref currentWidth);
}
};
while (true)
{
if (currentWidth < width)
{
for(int i=0;i<width;i++)
acc.BeginInvoke(null, null);
}
if (totalCalls % 1000 == 0)
Console.WriteLine("called {0:N}", totalCalls);
}
}
После запуска в течение примерно 20 минут и более 30 миллионов вызовов BeginInvoke позже потребление частной байтовой памяти стало постоянным (23 МБ), как и количество дескрипторов. Похоже, утечки нет. Я прочитал книгу Джеффри Рихтерса C # через CLR, где он заявляет об утечке памяти. По крайней мере, это кажется неверным с .NET 3.5 SP1.
Тестовая среда: Windows 7 x86 .NET 3.5 с пакетом обновления 1 Intel 6600 Dual Core 2,4 ГГц
С уважением, Алоис Краус
В некоторых ситуациях BeginInvoke не требует EndInvoke (особенно при обмене сообщениями окна WinForms). Но определенно существуют ситуации, когда это имеет значение - например, BeginRead и EndRead для асинхронной связи. Если вы захотите запустить BeginWrite и забыть его, через некоторое время вы, вероятно, столкнетесь с серьезными проблемами с памятью.
Итак, ваш единственный тест не может быть окончательным. Чтобы правильно ответить на ваш вопрос, вам необходимо иметь дело с разными типами делегатов асинхронных событий.
Рассмотрим следующий пример, который работал на моей машине в течение нескольких минут и достиг рабочего набора 3,5 ГБ, прежде чем я решил его убить.
Action a = delegate { throw new InvalidOperationException(); };
while (true)
a.BeginInvoke(null, null);
ПРИМЕЧАНИЕ: Обязательно запускайте его без подключен отладчик или отключены «прерывание при возникновении исключения» и «прерывание при необработанном пользователем исключении».
РЕДАКТИРОВАТЬ: Как указывает Джефф, проблема с памятью здесь не утечка, а просто случай перегрузки системы из-за постановки работы в очередь быстрее, чем она может быть обработана. В самом деле, то же самое поведение можно наблюдать, заменив бросок на любую подходящую длительную операцию. И использование памяти будет ограничено, если мы оставим достаточно времени между вызовами BeginInvoke.
Технически, это оставляет исходный вопрос без ответа. Однако, независимо от того, может ли это вызвать утечку, вызов Delegate.
От того, утечка памяти в настоящее время не зависит. Команда разработчиков фреймворка может в будущем изменить что-то таким образом, что может привести к утечке памяти, а поскольку официальная политика гласит: "Вы должны позвонить в EndInvoke", то это "по замыслу".
Неужели вы хотите рискнуть, что ваше приложение вдруг начнет утечку памяти когда-нибудь в будущем, потому что вы решили полагаться на наблюдаемое поведение, а не на документированные требования?
.