异步睡眠循环

Foi*_*itn 3 c# wait async-await

所以对于我的一个项目,我需要每 100 毫秒从一个硬件中获取值。获取需要 x 毫秒。所以我正在寻找但找不到的是一种确保 for/while 总是需要 100 毫秒(或更多)的方法。

所以在伪代码中:

for (int i = 0; i < 100; i++) // Take measurements for 10 seconds
{
    // Call some async method that sleeps for 100 ms
    this.temperatureList.Add(this.HardwareManager.GetSensorValue(ESensor.Temperature)); // Takes 15 ms
    this.temperatureList.Add(this.HardwareManager.GetSensorValue(ESensor.Pressure)); // Takes 30 ms
    // await async method to finish
}
Run Code Online (Sandbox Code Playgroud)

我已经尝试了一些异步等待的东西,但我似乎无法理解它。谁能帮我找到一种可靠的方法来以 100 毫秒的间隔进行 10 秒的测量?

编辑:我知道因为我在 Windows 上运行 C#,所以计时并不是一个真正有效的选择。但这在我的问题中并不重要。我的问题是,如何确保 for 循环至少需要(!!!)100 毫秒,同时尽可能接近 100 毫秒。

ang*_*son 5

一种方法是创建一个秒表,并在完成每个操作后等待它达到下一个里程碑。

这种方式的好处是:

  • 您将循环的开销添加到混音中,调整延迟以进行补偿
  • 因此,它应该几乎没有“漂移”(根据它们应该发生的时间,操作将逐渐“稍后”)

有点像这样:

var sw = Stopwatch.StartNew();
long next = 100;
while (sw.ElapsedMilliseconds < 10000)
{
    // do your operation here
    long left = next - sw.ElapsedMilliseconds;
    if (left > 0)
        await Task.Delay((int)left);
    next += 100;
}
Run Code Online (Sandbox Code Playgroud)

由于事物的准确性,您的操作将大约每 100 毫秒发生一次,但它们不应漂移。

如果您可以在这 10 秒内占用一个线程,则可以使用以下命令关闭延迟部分Thread.Sleep

if (left > 0)
    Thread.Sleep((int)left);
Run Code Online (Sandbox Code Playgroud)

它将运行您的操作接近 100 毫秒标记,但可能仍然有轻微的开销,因为它Thread.Sleep也有一些开销。

如果你对“燃烧 CPU”没意见,你可以这样做:

var sw = Stopwatch.StartNew();
var next = 100;
while (sw.ElapsedMilliseconds < 10000)
{
    // do your operation here
    
    while (sw.ElapsedMilliseconds < next) ;
    next += 100;
}
Run Code Online (Sandbox Code Playgroud)

这将使您的操作更接近实际的 100 毫秒标记,因为Task.Delay并且Thread.Sleep会有开销,往往会比您预期的延迟稍长。但是,它会在这 10 秒内占用 CPU 内核。

  • 添加 `await Task.Delay(1)` 会给你带来与第一部分相同的“大约 100 毫秒”的不准确性。也许在这些情况下使用 Thread.Sleep(...) 会减少开销。然后,您将占用一个线程 10 秒,但不会占用整个 CPU。 (2认同)