Почему ManualResetEventSlim.Wait не блокируется в течение полного времени ожидания?

Так что я использую System.Threading.ManualResetEventSlim в моем коде, и я случайно заметил, что иногда, когда я звоню Wait(TimeSpan) время ожидания значительно меньше указанного времени.

Вот модульный тест, который демонстрирует мою ситуацию

using System;
using System.Diagnostics;
using NUnit.Framework;

namespace DB
{
    [TestFixture]
    public class ManualResetEventSlimTests
    {
        [Test]
        [Repeat(100)]
        public void TestThatWait_ShouldBlockForAtLeastAsLongAsTheWaitTimeout_IfNotSignalled()
        {
            TimeSpan waitTime = TimeSpan.FromMilliseconds(250);
            using (var waiter = new System.Threading.ManualResetEventSlim(false))
            {
                var stopwatch = System.Diagnostics.Stopwatch.StartNew();
                waiter.Wait(waitTime);
                Assert.That(stopwatch.Elapsed, Is.GreaterThanOrEqualTo(waitTime));
            }
        }
    }
}

Этот тест будет проходить большую часть времени, но при повторении 100 раз всегда будет как минимум 1 сбой, поскольку время, измеренное секундомером, меньше указанного времени ожидания. Типичный сбой

Ожидается: больше или равно 00:00:00.2500000 Но было: 00:00:00.2497514

Моей первой мыслью было, что секундомер не достаточно точен, но, похоже, это не так; Stopwatch.Frequency = 3507511, что означает, что оно должно быть точным до примерно 285 нс на тик, т. Е. Намного, намного меньше, чем расхождение в 0,25 мс (при условии, что оно может точно рассчитывать тики).

Тот факт, что он ожидает на несколько долей мс меньше, чем я ожидал, не влияет на мою конкретную программу, но мне было любопытно, и мой Google-foo не обнаружил ничего уместного. Поэтому я поставил это перед SO-сообществом, чтобы узнать, есть ли у кого-нибудь разумное объяснение.

1 ответ

Решение

ManualResetEventSlim в конечном итоге использует Environment.TickCount (см. http://referencesource.microsoft.com/#mscorlib/system/threading/ManualResetEventSlim.cs,8a17ba6e95765ed8 и http://referencesource.microsoft.com/#mscorlib/system/threading/SpinWait.cs,9212529427afb371). Документы утверждают:

Обратите внимание, что, поскольку он получен из системного таймера, разрешение свойства TickCount ограничено разрешением системного таймера, которое обычно находится в диапазоне от 10 до 16 миллисекунд.

В качестве таких, Stopwatch скорее всего будет более точным, чем ManualResetEventSlim,

Другие вопросы по тегам