【问题标题】:Alternative to an infinite loop替代无限循环
【发布时间】:2013-05-15 19:39:19
【问题描述】:

我有一个无限循环:-

while(true)
{
//display an image
}

显然 CPU 上升了。

我用过:

Thread.Sleep(100);
Thread.Sleep(0);
ApplicationDoEvents() - I know i shouldn't

除非应用程序结束,否则此循环将永远不会结束。它们是无限循环的其他替代方案吗?

我正在用户控件中显示图像,并且我正在覆盖 OnPaint 事件...

if (CurrentFrame != null)
{
    g.DrawImageUnscaled(CurrentFrame, 0,0);
}



public void NewFrame(Image _currentFrame)
{
    if (CurrentFrame != null)
    {
        CurrentFrame.Dispose();
    }
    CurrentFrame = _currentFrame;
    Invalidate();
    //Update();  
}

欢迎任何见解/建议/建议...

【问题讨论】:

  • 您使用的是什么窗口工具包?绘图通常应该由窗口工具包调用(反过来由窗口系统调用)
  • 嗨,只是为了确认我对乔纳森的评论。我目前正在覆盖用户控件中的 onpaint 事件,并且我正在通过 onpaint 对象提供的图形对象绘制位图对象。我应该解释得更好一点,但我专注于无限循环
  • 我的重点是无限循环以及我可以采用的不同策略来代替它。我正在使用 c#。我应该在我原来的问题中说明这一点。显然,我通过无限循环“启动”的内容与 CPU 使用率有关,但无论我是否“启动”任何代码,我都会遇到 CPU 问题。
  • 你让我了解我是如何渲染我的图形以及在什么环境下渲染的,这是非常正确的。我的错真的我应该更好地详细说明我目前在做什么。不过真的很感谢你的时间。
  • 你能详细说明为什么你需要那个循环吗?

标签: c# timer schedule long-running-processes


【解决方案1】:

您可以在这里做几件事。第一种是使用计时器以标准速率进行更新。如果您使用的是 Windows 窗体,则可以在窗体上放置一个计时器组件并将周期设置为 15 毫秒。这将为您(大约)每秒提供 66 帧更新。

请注意,.NET 计时器最多可为您提供大约 15 毫秒的分辨率。您可以自行实现其中一个 Windows 计时器,以提供 1 毫秒的分辨率,但这样做会使您的代码变得非常复杂。

您可以做的另一件事是让更新CurrentFrame 图像的代码调用更新方法,并且让该更新方法的更新频率不超过某个设定速率。为此,您可以在程序开始时启动Stopwatch,并检查自上次更新以来经过的时间。比如:

private Stopwatch FrameTimer = Stopwatch.StartNew();
private long LastUpdateTime = 0;
private const long MinUpdateMs = 10; // minimum time between updates

void DoUpdate()
{
    long currentTime = FrameTimer.ElapsedMilliseconds;
    if ((currentTime - LastUpdateTime) < MinUpdateMs)
    {
        // updated within the last 10 ms
        return;
    }
    LastUpdateTime = currentTime;
    if (CurrentFrame != null)
    {
        g.DrawImageUnscaled(CurrentFrame, 0,0);
    }
}

第二个选项非常有效,但如果没有持续更改,您将面临无法获得最新更新的风险。如果您的CurrentFrame 更新之间可能有很长的时间间隔,那么您可以有一个计时器强制每隔一秒左右更新一次。如果你这样做,你想在DoUpdate 方法中添加一些同步来防止并发更新。我建议:

private object UpdateLock = new object();
void DoUpdate()
{
    if (!Monitor.TryEnter(UpdateLock))
    {
        // update already in progress
        return;
    }
    try
    {
        long currentTime = FrameTimer.ElapsedMilliseconds;
        if ((currentTime - LastUpdateTime) < MinUpdateMs)
        {
            // updated within the last 10 ms
            return;
        }
        LastUpdateTime = currentTime;
        if (CurrentFrame != null)
        {
            g.DrawImageUnscaled(CurrentFrame, 0,0);
        }
    }
    finally
    {
        Monitor.Exit(UpdateLock);
    }
}

【讨论】:

  • 我很喜欢你的回答。谢谢吉姆。
【解决方案2】:

当您需要线程休眠直到某些条件成立时,通常的做法是使用condition variable。线程锁定互斥体,然后将其传递给条件变量并阻塞(然后暂时释放互斥体上的锁)。然后,当它等待的条件为真时,使该条件为真的线程向条件变量发出信号,该线程唤醒,重新获得互斥锁上的锁,并继续其愉快的方式。不过要注意的是条件变量可能会产生虚假唤醒(有时多个线程可能会在同一条件下等待),因此需要有某种标志来指示条件何时为真,并且当线程从条件变量中唤醒时,它会检查该标志并在标志不为真时再次阻塞条件变量。与条件变量一起使用的互斥锁也用于保护该标志。

D 运行时在core.sync.condition 中有一个条件变量。

还有其他关于堆栈溢出条件变量的好问题,例如:condition variable

现在,对于您的特定用例,我必须同意 Idan Arye 的评论,即您尝试使用窗口工具包进行绘画似乎有点离题。通常,它的工作方式是覆盖OnPaint 函数(或任何特定工具包调用的函数),然后窗口工具包为您调用该函数。你不会告诉它自己画,也不必担心等待条件或类似的事情。所以,听起来您可能误解了如何使用您正在使用的窗口工具包。

【讨论】:

  • 您好 Jonathan 和 Idan,感谢您的回复。在研究条件变量解释的同时,我将讨论“窗口工具包”。我正在覆盖用户控件中的绘画。 NewFrame “更新/更改”当前位图和 onpaint 事件(通过图形对象)并在图像中绘制。这不是做事的正确方法吗?我可能没有正确解释这一点,或者真的搞错了?
  • 嗨乔纳森,我正在使用 C#,但我发现了这种同步条件的等价物。我之前确实看过它,但不确定如何应用它或它是否是我需要的。您的回答帮助我现在专注于使用它 - 谢谢。
  • CPU 使用率还不错(12%),但在朋友的旧笔记本电脑上,它在 80% 左右。显然,可用的 RAM 和时钟速度是有影响的。如果我必须指定某个基本的 PC 规格,那么这就是我必须做的。我只是确保我有效地编码。我确实有一个 volatile 条件变量,它正在我的循环中进行检查,这确实有点帮助。我还从 while(true) 循环切换到 Do while(true) 循环,因为有人告诉我这是一种更好的方法(与循环中的条件顺序有关,有助于编译器忽略这一点)一个无限循环)。
  • 陪审团仍然在那个问题上。正如我所说,我现在将看看这个 monitior.pulse/monitior.wait 消费者/生产者策略。如果我仍然没有意义,请随时告诉我:)
  • 很抱歉在 sn-ps 中发表评论,我似乎可以一口气上传所有我想要的东西。另外,语法不是我的事……
猜你喜欢
  • 2011-01-06
  • 2016-12-21
  • 2012-04-28
  • 2023-03-20
  • 1970-01-01
  • 2014-10-31
  • 2012-03-15
  • 2015-01-27
  • 1970-01-01
相关资源
最近更新 更多