Я считаю, что я понимаю, что такое закрытие для анонимной функции и знакомо с традиционными ловушками. Хорошие вопросы, касающиеся этой темы, - here и here. Цель состоит не в том, чтобы понять, почему и как это работает в общем смысле, но и для того, чтобы судить о тонкостях, о которых я могу не знать, в зависимости от поведения генерируемых ссылок класса замыкания. В частности, какие ошибки существуют, когда сообщается о поведении измененной извне переменной, зафиксированной в закрытии?Является ли воплощение в зависимости от доступа к модифицированному закрытию нежелательным?
Пример
У меня есть долгоиграющий, массивно параллельная служба работника, который имеет ровно один случай ошибки - когда он не может восстановить работу. Степень параллелизма (количество используемых концептуальных потоков) настраивается. Обратите внимание: концептуальные потоки реализованы как задачи <> через TPL. Поскольку служба постоянно работает, пытаясь получить работу при умножении на неизвестную степень параллелизма, это может означать, что от тысячи до десятков тысяч ошибок могут быть сгенерированы в секунду.
Таким образом, мне нужен механизм отчетности, который сроки, а не попытку переплете, который изолирован на свою собственную концептуальную нить, и это сократимое. С этой целью я разработал рекурсивную задач лямбда, который обращается мой счетчик ошибок каждые 5 минут за пределами первичной попытки на основе зацикливания, который пытается получить работу:
var faults = 1;
Action<Task> reportDelay = null;
reportDelay =
// 300000 is 5 min
task => Task.Delay(300000, cancellationToken).ContinueWith(
subsequentTask =>
{
// `faults` is modified outside the anon method
Logger.Error(
$"{faults} failed attempts to get work since the last known success.");
reportDelay(subsequentTask);
},
cancellationToken);
// start the report task - runs concurrently with below
reportDelay.Invoke(Task.CompletedTask);
// example get work loop for context
while (true)
{
object work = null;
try
{
work = await GetWork();
cancellationToken.Cancel();
return work;
}
catch
{
faults++;
}
}
Опасения по
Я понимаю, что, в этом случае сгенерированное замыкание с точкой по ссылке на мою переменную faults
(которая увеличивается, когда какой-либо концептуальный поток пытается получить работу, но не может). Я также понимаю, что это обычно обескураживает, но из того, что я могу сказать, только потому, что это приводит к неожиданному поведению, когда кодируется, ожидая закрытия, чтобы зафиксировать значение.
Здесь я хочу и полагаюсь на замыкание, фиксируя переменную faults
по ссылке. Я хочу сообщить значение переменной во время вызова продолжения (это не обязательно должно быть точным). Я слегка обеспокоен тем, что faults
преждевременно GC'd, но я отменяю цикл, прежде чем выйти из этого лексического масштаба, заставляя меня думать, что он должен быть безопасным. Есть ли что-нибудь еще, о чем я не думаю? Какие опасности существуют при рассмотрении доступа к закрытию за пределами изменчивости базовой стоимости?
Ответ и объяснение
Я приняли ответ ниже, refactors кода, чтобы избежать необходимости доступа замыкания пути материализации монитора ошибок в свой класс. Однако, поскольку это не отвечает на вопрос напрямую, я буду включать краткое объяснение здесь для будущих читателей о надежном поведении:
До тех пор, пока закрытая переменная остается в пределах срока действия крышки, она можно полагаться на то, чтобы вести себя как истинная ссылочная переменная. Опасности доступа к переменной модифицированную во внешней области видимости изнутри крышки являются:
- Вы должны понимать, что переменная будет вести себя в качестве эталона в рамках закрытия, мутирует его значение, как он изменяется во внешнем объеме ,Переключатель закрытия всегда будет содержать текущее значение времени выполнения внешней переменной области видимости, не значение в момент создания замыкания.
- Вы должны написать свою программу таким образом, чтобы гарантировать, что время жизни внешней переменной будет таким же или больше, чем сама анонимная функция/закрытие. Если вы мусор собираете внешнюю переменную, то ссылка станет недопустимым указателем.
Почему вы создаете анонимную функцию только для ее вызова в первую очередь? Просто запустите код, не используя анонимную функцию. – Servy
Поскольку a) его цикл так «вызывается» один раз, но работает непрерывно до отмены, и b) без обращения к этой переменной «ошибка» механизм отчета бесполезен. Поскольку первичный цикл является _attempt-bound_ (т. Е. Он зацикливается при попытке выполнить попытку), цикл отчета должен быть каким-то независимым (в противном случае он также будет связан с попытками), а также требует ссылки на эту переменную, чтобы включить его в Ошибка. –
@Servy, он называет эту функцию рекурсивно, а не только один раз. – Evk