Кэшируются ли переменные, используемые System.Threading.Timer?

Я пытаюсь устранить неполадки в процессе, который таинственным образом останавливается, и я пытаюсь определить, может ли у нас возникнуть проблема с кэшированием переменной и ее повторным чтением.

В моем сценарии у меня есть операция, которая выполняется по таймеру (я назову ее Poll), и пара внешних функций, которые вызываются, чтобы сигнализировать о начале и остановке внешней операции. Из-за деталей, которые не стоит вдаваться в подробности, этот гипотетический класс не хочет, чтобы операция Poll выполнялась, если выполняется внешняя операция. Кроме того, внешняя операция может выполняться несколько раз одновременно, поэтому я использую счетчик, чтобы указать количество выполняемых операций.

Текущая логика выглядит более или менее похоже на этот упрощенный пример:

class MyClass
{
    private int operationsInProgress;

    private void Poll() // Pretend we have a timer in place that's calling this periodically
    {
        var inProgress = operationsInProgress;

        if (inProgress > 0) return;

        DoSomething();
    }

    public void StartExternalOperation()
    {
        Interlocked.Increment(ref operationsInProgress);
    }

    public void EndExternalOperation()
    {
        Interlocked.Decrement(ref operationsInProgress);
    }
}

Предположения:

  • И StartExternalOperation, и EndExternalOperation вызываются одинаковое количество раз — они выполняются внутри try...finally, и нет пути, который оставил бы осиротевший Start там.
  • В какой-то момент после неопределенного количества выполнений DoSomething() больше никогда не вызывается. В моем реальном сценарии я не знаю точно почему, но я устранил все другие причины, которые смог найти, кроме этой.

Возможно ли, что я не использую какой-либо забор (ни через переменную Interlocked, ни через переменную volatile) в операции чтения, что значение кэшируется, а фактическая переменная больше не читается?

Поскольку это проблема, которую мы не можем надежно воспроизвести, мне нужно знать, может ли то, что я подозреваю, быть причиной. Я мог бы изменить код, чтобы использовать что-то более формальное, например ReaderWriterLock (или Slim), и я знаю, что у меня будет правильная логика, мне просто нужно знать, может ли то, что я описал, быть законной причиной.


person Adam Robinson    schedule 23.12.2016    source источник
comment
Вы уверены, что Poll вызывается? Я видел что-то подобное при использовании System.Timers.Timer, и оказалось, что таймер выдавал исключение (о котором никогда не сообщалось, потому что обработчик событий таймера подавляет исключения). Но вы сказали, что используете System.Threading.Timer. Хм . . . Может быть, Interlocked.Add(ref operationsInProgress, 0); будет полезен?   -  person Jim Mischel    schedule 23.12.2016
comment
@JimMischel Это была возможность, которую мы рассмотрели (и не можем окончательно исключить, кроме того, что это не влияет ни на что другое), но на данный момент у нас нет ничего, что указывало бы на возможность того, что это не так. звонят. Функция здесь упрощена для демонстрации логики, но в фактической реализации есть надежная обработка исключений и ведение журнала.   -  person Adam Robinson    schedule 23.12.2016
comment
Кажется разумным поместить строку журнала в этот метод или использовать какие-либо другие средства доказательства того, что метод все еще выполняется.   -  person Jim Mischel    schedule 23.12.2016
comment
Похоже проблема не в логике, а в реализации. Если не появится кто-то, кто распознает точную проблему и может дать точное решение, я не уверен, что можно многое сделать для решения вашей проблемы. Все, что я могу сказать, это регистрировать это более тщательно, чем вы делаете сейчас, и смотреть, не появится ли закономерность.   -  person Abion47    schedule 23.12.2016
comment
@JimMischel: На самом деле есть вызов ведения журнала, если он пропускает операцию, но он регистрируется как подробный, и его включение приведет к записи горы другой отладочной информации ... хотел бы попробовать в качестве следующего шага.   -  person Adam Robinson    schedule 23.12.2016
comment
@Abion47: Вопрос конкретно касается того, подлежит ли функция выполнения System.Threading.Timer кэшированию энергонезависимых переменных (аналогично тому, как если бы вы запустили поток с бесконечным циклом и проверили значение этой переменной внутри цикла ).   -  person Adam Robinson    schedule 23.12.2016
comment
Я никогда не видел случая, когда в этом случае переменная кэшируется. Это не значит, что этого не может быть, но я считаю это маловероятным. Я считаю более вероятным, что либо ваше утверждение № 1 выше неверно, либо что-то препятствует вызову метода Poll().   -  person Jim Mischel    schedule 23.12.2016
comment
У меня возникает соблазн закрыть как дубликат этот вопрос.   -  person Mike Zboray    schedule 23.12.2016
comment
Это вечный вопрос о System.Threading.Timer, классе, объекты которого подлежат сборке мусора. Если вы не убедитесь, что объект Timer явно используется, он будет собран и, конечно же, перестанет тикать. Стандартная ошибка — иметь только одну ссылку на объект и делать ее локальной переменной метода. Фрагмент кода отсутствует.   -  person Hans Passant    schedule 23.12.2016
comment
@HansPassant: я ценю то, что вы говорите, но, пожалуйста, имейте в виду, что то, о чем я спрашиваю, касается конкретно того, подлежит ли значение кэшированию; если бы я спрашивал, почему мой таймер останавливается?, я бы включил такую ​​​​деталь :). Но включение достаточного количества деталей для устранения ЭТОЙ проблемы было бы непрактично и, вероятно, было бы полезно только мне (вместо того, чтобы ответить на общий вопрос).   -  person Adam Robinson    schedule 23.12.2016