本文介绍了是否有过期Task.Delay`的'变体的实时传递例如后甚至当系统挂起和恢复?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有它更有意义,我有一个延迟的周期性动作等待在现实世界中时间量来传递,而不是等待系统时钟滴答作响的时候一定数目的情况。这样我可以,比方说,续订租约被跟踪在不同的系统/实时被超时的实时传递。

I have a situation where it makes more sense to me to have a delay between periodic actions wait for an amount of time in the real world to pass rather than waiting for the system clock to tick some number of times. This way I could, say, renew a lease being tracked on a different system/being timed out in real time after some amount of real time passes.

我怀疑一定量后 Task.Delay 可能已经有这种行为,但我想确认,所以我写了一个测试程序(见下文)。我的发现是, Task.Delay 行为完全不同,当系统被挂起和恢复。从观察其行为, Task.Delay 充当如果它:

I suspected that Task.Delay might already have this behavior, but I wanted to make sure, so I wrote a test program (see below). My discovery was that Task.Delay behaves quite differently when the system is suspended and resumed. From observing its behavior, Task.Delay acts as if it:


  • 设置计数器计时器滴答数所需的时间这个量来传递。

  • 每个一些计时器滴答时间计数器递减。

  • 标记本身当计数器为0。

  • 完成
  • Sets a counter to the number of timer ticks necessary for this amount of time to pass.
  • Decrements that counter each time some timer ticks.
  • Marks itself as completed when the counter reaches 0.

有没有一种办法等待中经过这样的一些实时的通过量,这样,如果以后延迟将已经过期延续我可以触发恢复系统或过程,我可以运行一个任务的方法吗?眼下,作为一种解决方法,我只是继续每当无论是 Task.Delay 到期或 SystemEvents.PowerModeChanged 恢复。这是处理问题的正确方法是什么?这似乎很奇怪,我不得不撰写用于不同目的的这样两种API,我很惊讶地看到, SystemEvents.PowerModeChanged 存在。另外,我担心这种API,在的Microsoft.Win32 命名空间中,可能无法移植。

Is there a way to await in such a way that I can run a task after some amount of real time passes so that if the system or process is resumed after the delay would have expired my continuation can be triggered? Right now, as a workaround, I’m just continuing whenever either Task.Delay expires or SystemEvents.PowerModeChanged fires Resume. Is this the correct way to handle the situation? It seems odd to me to have to compose two APIs intended for different purposes this way and I was surprised to see that SystemEvents.PowerModeChanged exists. Also, I fear that this API, being in the Microsoft.Win32 namespace, may not be portable.

using Microsoft.Win32;
using System;
using System.Threading.Tasks;

class Program
{
    static int Main(string[] args) => new Program().Run(args).Result;

    async Task<int> Run(string[] args)
    {
        SystemEvents.PowerModeChanged += (sender, e) => Console.WriteLine($"{e}: {e.Mode}");
        var targetTimeSpan = TimeSpan.FromSeconds(20);
        var start = DateTime.UtcNow;
        var task = Task.Delay(targetTimeSpan);
        var tickerTask = Tick(targetTimeSpan);
        Console.WriteLine($"Started at {start}, waiting {targetTimeSpan}.");
        await task;
        var end = DateTime.UtcNow;
        Console.WriteLine($"Ended at {end}, waited {end - start}.");
        await tickerTask;
        return 0;
    }
    async Task Tick(TimeSpan remaining)
    {
        while (remaining > TimeSpan.Zero)
        {
            Console.WriteLine($"tick: {DateTime.UtcNow}");
            await Task.Delay(TimeSpan.FromSeconds(1));
            remaining -= TimeSpan.FromSeconds(1);
        }
    }
}

在我的计划,我设置任务 Task.Delay(TimeSpan.FromSeconds(20))。然后,我用它运行20次( tickerTask )。

In my program, I set task to a Task.Delay(TimeSpan.FromSeconds(20)). I then also print the current date once every second (plus a small amount of time) using a loop which runs 20 times (tickerTask).

一个系统的输出暂停简历:

The output for a system suspend resume is:

tick: 2016-07-05 A.D. 14:02:34
Started at 2016-07-05 A.D. 14:02:34, waiting 00:00:20.
tick: 2016-07-05 A.D. 14:02:35
tick: 2016-07-05 A.D. 14:02:36
tick: 2016-07-05 A.D. 14:02:37
tick: 2016-07-05 A.D. 14:02:38
tick: 2016-07-05 A.D. 14:02:39
tick: 2016-07-05 A.D. 14:02:40
tick: 2016-07-05 A.D. 14:02:41
Microsoft.Win32.PowerModeChangedEventArgs: Suspend
tick: 2016-07-05 A.D. 14:02:42
tick: 2016-07-05 A.D. 14:02:44
tick: 2016-07-05 A.D. 14:03:03
Microsoft.Win32.PowerModeChangedEventArgs: Resume
tick: 2016-07-05 A.D. 14:03:05
tick: 2016-07-05 A.D. 14:03:06
tick: 2016-07-05 A.D. 14:03:08
tick: 2016-07-05 A.D. 14:03:09
tick: 2016-07-05 A.D. 14:03:10
tick: 2016-07-05 A.D. 14:03:11
tick: 2016-07-05 A.D. 14:03:12
Ended at 2016-07-05 A.D. 14:03:13, waited 00:00:38.8964427.
tick: 2016-07-05 A.D. 14:03:13
tick: 2016-07-05 A.D. 14:03:14

正如你所看到的,我中止我的电脑在14时02分44秒和14时03分03秒恢复它。此外,你可以看到, Task.Delay(TimeSpan.FromSeconds(20))表现大致相同,在 Task.Delay循环20次(时间跨度.FromSeconds(1))。 38.9秒的总等待时间是大约20秒加18秒(03:03减去02:44)的睡眠时间。我希望,总的等待时间会恢复之前的时间加上睡眠时间:28秒或10(02:44 02:34负)加18秒(03:03 02:44负)

As you can see, I suspended my computer at 14:02:44 and resumed it at 14:03:03. Further, you can see that Task.Delay(TimeSpan.FromSeconds(20)) behaved roughly the same as looping 20 times over Task.Delay(TimeSpan.FromSeconds(1)). The total wait time of 38.9 seconds is roughly 20 seconds plus the sleep time of 18 seconds (03:03 minus 02:44). I was hoping that the total wait time would be the time prior to resume plus the sleep time: 28 seconds or 10 (02:44 minus 02:34) plus 18 seconds (03:03 minus 02:44).

当我使用Process Explorer的挂起和恢复的过程中, Task.Delay()做的真正忠实地完成20秒后,时间。不过,我不能肯定进程浏览器实际上是暂停所有的进程的线程的正确,也许消息泵继续运行?然而,这个过程的具体情况被暂停和外部的恢复既没有真正的东西大多数开发人员会尽力支持也不是从正常的进程调度不同(其中 Task.Delay()预计处理)。

When I use Process Explorer to suspend and resume the process, the Task.Delay() does faithfully complete after 20 seconds of real time. However, I am not certain that Process Explorer is actually suspending all of the threads of my process properly—maybe the message pump continues to run? Yet, the particular case of the process being suspended and resumed externally is both not really something most developers would try to support nor is it that different from normal process scheduling (which Task.Delay() is expected to handle).

推荐答案

一个简单的解决办法是写一个定期检查当前时间的方法和当从开始时间差达到所需量的完成:

A simple solution would be to write a method that periodically checks the current time and completes when difference from the start time reaches the desired amount:

public static Task RealTimeDelay(TimeSpan delay) =>
    RealTimeDelay(delay, TimeSpan.FromMilliseconds(100));

public static async Task RealTimeDelay(TimeSpan delay, TimeSpan precision)
{
    DateTime start = DateTime.UtcNow;
    DateTime end = start + delay;

    while (DateTime.UtcNow < end)
    {
        await Task.Delay(precision);
    }
}



什么精度你应该使用依赖,那么,你需要的精确度,你需要(尽管这可能不会是一个问题)的性能。如果你的延迟将要在秒的范围内,那么几百毫秒的精密度听起来很合理的我。

What precision you should use depends on, well, the precision you require and the performance you need (though this likely isn't going to be a problem). If your delays are going to be in the range of seconds, then precision of hundreds of milliseconds sounds reasonable to me.

请注意,如果这个解决方案将无法正常工作时间在电脑上的变化(但DST过渡或其它时区的变化是很好的,因为它使用 UtcNow )。

Note that this solution won't work correctly if the time on the computer changes (but DST transitions or other timezone changes are fine, since it's using UtcNow).

这篇关于是否有过期Task.Delay`的'变体的实时传递例如后甚至当系统挂起和恢复?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-24 08:16