Сериализованная задача реактивного расширения, каждая из которых может быть отменена

Я попытался ответить на другой вопрос с помощью расширения Rx. Пока я делал решение, я обнаружил кое-что странное.

static Random rand = new Random();

static void Main(string[] args) {
    //var obs = Observable.Interval(TimeSpan.FromMilliseconds(250)).Do<long>(i =>
    var obs = Observable.Interval(TimeSpan.FromMilliseconds(25)).Do<long>(i =>
    {
        CancellationTokenSource source = new CancellationTokenSource(25);
        //CancellationTokenSource source = new CancellationTokenSource(250);
        ReadNext(source.Token, i);
    }).Publish();
    var disp = obs.Connect();
    Console.ReadKey();
    disp.Dispose();
    Console.ReadKey();
}

static private void ReadNext(CancellationToken token, long actual) {
    int i = rand.Next(4);
    Stopwatch watch = new Stopwatch();
    watch.Start();
    for(int j = 0; j < i; j++) {
        //Thread.Sleep(100);
        Thread.Sleep(10);
        if(token.IsCancellationRequested) {
            Console.WriteLine(string.Format("method cancelled. cycles: {0}, should be 3. Now should be last (2): {1}", i, j));
            return;
        }
    }
    Console.WriteLine(string.Format("method done in {0} cycles. Preserved index: {1}. Elapsed time: {2}", i, actual, watch.ElapsedMilliseconds));
    watch.Stop();
}

Возникла проблема с тайм-аутом отмены. Каким-то образом, когда происходит третий цикл (мы уже ждали ~30 миллисекунд), ReadNext не отменяется каждый раз.

Посмотрите распечатку:

method done in 1 cycles. Preserved index: 7. Elapsed time: 9
method done in 1 cycles. Preserved index: 8. Elapsed time: 9
method done in 0 cycles. Preserved index: 9. Elapsed time: 0
method cancelled. cycles: 3, should be 3. Now should be last (2): 2
method done in 1 cycles. Preserved index: 11. Elapsed time: 9
method done in 2 cycles. Preserved index: 12. Elapsed time: 19
method done in 2 cycles. Preserved index: 13. Elapsed time: 19
method done in 0 cycles. Preserved index: 14. Elapsed time: 0
method done in 2 cycles. Preserved index: 15. Elapsed time: 19
method done in 0 cycles. Preserved index: 16. Elapsed time: 0
method done in 1 cycles. Preserved index: 17. Elapsed time: 9
method cancelled. cycles: 3, should be 3. Now should be last (2): 2
method done in 1 cycles. Preserved index: 19. Elapsed time: 9
method done in 3 cycles. Preserved index: 20. Elapsed time: 29  <- bug.
method done in 2 cycles. Preserved index: 21. Elapsed time: 19
method done in 1 cycles. Preserved index: 22. Elapsed time: 9
method done in 1 cycles. Preserved index: 23. Elapsed time: 9
method done in 2 cycles. Preserved index: 24. Elapsed time: 19
method done in 2 cycles. Preserved index: 25. Elapsed time: 19
method done in 2 cycles. Preserved index: 26. Elapsed time: 19
method done in 1 cycles. Preserved index: 27. Elapsed time: 10
method done in 1 cycles. Preserved index: 28. Elapsed time: 9
method done in 3 cycles. Preserved index: 29. Elapsed time: 29  <- bug.
method done in 1 cycles. Preserved index: 30. Elapsed time: 9

Должен ли я слушать какой-то другой планировщик, чтобы быть уверенным, что через 25 миллисекунд токен отмены обязательно отменяется, или что-то другое вызывает ошибку?

РЕДАКТИРОВАТЬ

Если я обновлю порядок сна на единицу (проверьте код с комментариями выше), он сработает. Проблема в том, что Thread.Sleep недостаточно точен.


person ntohl    schedule 03.08.2015    source источник
comment
Я думаю, проблема в том, что Thread.Sleep недостаточно точен. См. этот вопрос   -  person juharr    schedule 03.08.2015
comment
Проверяю с увеличением сна на порядок. Можно ли как-то наблюдать, или изменить Thread.sleep, чтобы ожидание было на той же ветке с отменой?   -  person ntohl    schedule 03.08.2015
comment
Подтверждено, что Thread.sleep недостаточно точен. Какой поток/планировщик он использует?   -  person ntohl    schedule 03.08.2015


Ответы (1)


Windows не является ОС реального времени. Как правило, системные таймеры работают с частотой 60 Гц, что означает, что они имеют точность только до 16,7 мс. Добавьте к этому тот факт, что потоков гораздо больше, чем физических ядер для их запуска, и вы просто не можете ожидать написания кода с точным временем, не зная, что вы делаете.

Итак, всякий раз, когда вы пишете код таймера, просто предполагайте, что любой таймер, который вы запускаете, будет срабатывать с точностью +/- 16 мс.

person Brandon    schedule 03.08.2015