【问题标题】:Task Delay vs Thread Sleep Resolution Accuracy任务延迟与线程睡眠分辨率精度
【发布时间】:2016-06-22 21:28:53
【问题描述】:

我需要在特定时间后执行一个函数,因此我使用此代码。

_start = DateTime.Now;
await Task.Delay(_app.Settings.AudioFileStartVarianz.BaseSpan).ContinueWith(x => 
        {
            Console.WriteLine(DateTime.Now - _start);

        });

假设我想等待正好 0.04 秒。我现在的问题是它的工作不够精确。调用函数 5 次后得到以下输出:

  • 00:00:00.0414220
  • 00:00:00.0536098
  • 00:00:00.0507841
  • 00:00:00.0467757
  • 00:00:00.0425790

如果我使用此代码,效果会更好

        _start = DateTime.Now;
        Thread.Sleep(_app.Settings.AudioFileStartVarianz.BaseSpan);
        Console.WriteLine(DateTime.Now - _start);
  • 00:00:00.0405879
  • 00:00:00.0404284
  • 00:00:00.0402117
  • 00:00:00.0404908
  • 00:00:00.0409088

但现在我遇到了问题,该函数没有异步运行,这是什么不好,因为我正在播放音频文件 (NAudio)。

有什么想法可以在这里等待异步,以便我的音频文件不会停止吗?

韩国曼努埃尔

【问题讨论】:

标签: c# multithreading asynchronous sleep


【解决方案1】:

Thread.SleepTask.Delay 这两个调用的区别在于Thread.Sleep 调用一个OS 方法使线程休眠,Task.Delay 创建一个Timer 来模拟延迟。

调用是有效的......

private static extern int WaitOneNative(SafeHandle waitableSafeHandle, uint millisecondsTimeout, bool hasThreadAffinity, bool exitContext);

...和...

promise.Timer = new Timer(state => ((DelayPromise)state).Complete(), promise, millisecondsDelay, Timeout.Infinite);

...分别。

现在 Windows 中的计时器的分辨率非常低 - 大约有 15 毫秒的错误。我认为正是因为如此,你才会得到你的结果。

坏消息是,正如您所说,休眠线程不是异步的,因此要获得异步,您需要接受计时器解析错误。我认为你没有任何理智的方法可以避免它。

【讨论】:

  • 不是 Windows 计时器限制在 15 毫秒左右的分辨率。相反,Windows 计时器周围的 .NET 包装器施加了这种限制。
  • @JimMischel - 我的理解是限制是操作系统的核心(设置为 1/64 秒或 15.625 秒),但可以更改。阅读randomascii.wordpress.com/2013/07/08/…。改变它的问题是功耗,因此便携式设备的电池寿命。
  • 是的,我读过。但至少在我使用过的每台台式机和笔记本电脑上,如果我创建一个周期为 1 毫秒的定时器队列定时器,我会得到 1 毫秒。
【解决方案2】:

要获得准确的计时器,您应该使用multimedia timer

多媒体计时器服务允许应用程序安排计时器事件 具有硬件可能的最大分辨率(或精度) 平台。这些多媒体计时器服务允许您安排计时器 比其他计时器服务更高分辨率的事件。

你可以在那里找到一个实现:

http://www.codeproject.com/Articles/5501/The-Multimedia-Timer-for-the-NET-Framework

所以对于你的例子来说,它可能是这样的

编辑代码简化:

    Multimedia.Timer mmTimer = new Multimedia.Timer(new Container())
    {
        Mode = Multimedia.TimerMode.OneShot,
        Period = 40,
        Resolution = 1,
        SynchronizingObject = this
    };
    mmTimer.Tick += new System.EventHandler(this.mmTimer_Tick);
    startTime = DateTime.Now;
    mmTimer.Start();

    private void mmTimer_Tick(object sender, System.EventArgs e)
    {           
        MessageBox.Show("ticks after 40ms");
    }

不要尝试使用 DateTime 检查计时器是否正确计时。现在,根据系统的不同,其精度约为 15 或 16 毫秒。

延伸阅读:http://www.nullskull.com/articles/20021111.asp

【讨论】:

  • 多媒体计时器已被弃用十年。 不要使用它们。 定时器队列定时器更灵活,使用更少的系统资源,并提供与多媒体定时器相同的准确性。有关实现和示例,请参阅informit.com/guides/content.aspx?g=dotnet&seqNum=817。或者,使用线程池计时器。但是请不要使用多媒体计时器。
猜你喜欢
  • 2013-03-03
  • 2012-04-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多