是一个没有延迟的无限循环不良练​​习吗?

时间:2017-12-16 11:52:10

标签: c++ performance loops sdl

简而言之: 与延迟减慢的类似循环相比,未延迟的while循环是否会消耗大量处理能力?

在不那么短的时间内:

我经常遇到这个问题。我正在编写一个程序的核心部分(微控制器单元或计算机应用程序),它包含一个半无限的while循环,以保持活力并查找事件。

我将采用这个例子:我有一个使用SDL窗口和控制台的小应用程序。在while循环中,我想听这个SDL窗口的事件,但是我还希望根据命令行输入通过全局变量来打破这个循环。可能的解决方案(伪代码):

// Global
bool running = true;

// ...

while (running)
{
    if (getEvent() == quit)
    {
        running = false;
    }
}

shutdown();

核心while循环将从已侦听的事件或外部事件中退出。 然而,这个循环连续运行,甚至可能每秒1000次。这有点过度杀戮,我不需要那个响应时间。因此,我经常添加一个延迟声明:

while (running)
{
    if (getEvent() == quit)
    {
        running = false;
    }
    delay(50); // Wait 50 milliseconds
}

这将刷新率限制为每秒20次,这很多。

所以。两者之间真的有区别吗?这很重要吗?在微控制器单元上是否更重要(处理能力非常有限(除了程序需要运行之外别无其他......)?

7 个答案:

答案 0 :(得分:4)

嗯,实际上这不是关于C ++的问题,而是答案取决于CPU架构/主机OS /延迟()实现。

  1. 如果它是一个多任务环境,那么delay()可以(并且可能会)帮助OS调度程序更有效地完成其工作。然而,真正的差异可能太少而无法通知(除了必须延迟()的旧的合作多任务处理。

  2. 如果它是单任务环境(可能是某个微控制器),那么如果底层实现能够执行一些专用的低功耗指令而不是普通的循环,则delay()仍然有用。但是,当然,除非您的手册明确说明,否则无法保证,

  3. 考虑到性能问题,很明显,你可以接收和处理一个有明显延迟的事件(甚至完全错过它),但是如果你认为它不是一个案例那么就没有其他的延迟()。

答案 1 :(得分:1)

你将使你的代码更难以阅读,你正在以旧式的方式进行异步:你明确地等待某些事情发生,而不是依赖于为你完成工作的机制。 此外,你延迟50毫秒。它总是最佳的吗?是否取决于正在运行的程序? 在C ++ 11中,您可以使用condition_variable。这允许您等待事件发生,而无需编写等待循环的编码。

此处的文档: http://en.cppreference.com/w/cpp/thread/condition_variable

我已经调整了这个例子,使其更容易理解。等待单个活动。

以下是适合您背景的示例

// Example program
#include <iostream>
#include <string>
#include <iostream>
#include <string>
#include <thread>
#include <mutex>
#include <chrono>
#include <condition_variable>

std::mutex m;
std::condition_variable cv;
std::string data;
bool ready = false;
bool processed = false;
using namespace std::chrono_literals;

void worker_thread()
{
    // Wait until main() sends data
    std::unique_lock<std::mutex> lk(m);

    std::cout << "Worker thread starts processing data\n";

    std::this_thread::sleep_for(10s);//simulates the work

    data += " after processing";

    // Send data back to main()
    processed = true;
    std::cout << "Worker thread signals data processing completed"<<std::endl;
    std::cout<<"Corresponds to you getEvent()==quit"<<std::endl;

    // Manual unlocking is done before notifying, to avoid waking up
    // the waiting thread only to block again (see notify_one for details)
    lk.unlock();
    cv.notify_one();
}

int main()
{
    data = "Example data";

    std::thread worker(worker_thread);    
    // wait for the worker
    {
        std::unique_lock<std::mutex> lk(m);

        //this means I wait for the processing to be finished and I will be woken when it is done. 
        //No explicit waiting
        cv.wait(lk, []{return processed;});
    }

    std::cout<<"data processed"<<std::endl;
}

答案 2 :(得分:1)

根据我的经验,你必须做一些放弃处理器的事情。睡眠工作正常,在大多数Windows系统上,即使是睡眠(1)也足以完全卸载处理器。

但是,如果你使用像std :: condition_variable这样的东西,你可以获得最好的世界。可以使用条件变量(类似于Windows API中的'events'和WaitForSingleObject)来构建构造。

一个线程可以阻塞另一个线程释放的条件变量。这样,一个线程可以执行condition_varaible.wait(some_time),它将等待超时时间(不加载处理器),或者当另一个线程释放它时它会立即继续执行。

我使用此方法,其中一个线程正在向另一个线程发送消息。我希望接收线程尽快响应,而不是在等待睡眠(20)完成后。例如,接收线程具有condition_variable.wait(20)。发送线程发送消息,并执行相应的condition_variable.release()。接收线程将立即释放并处理该消息。

此解决方案可以非常快速地响应消息,并且不会过度加载处理器。

如果你不关心可移植性,而你恰好使用windows,事件和WaitForSingleObject做同样的事情。

你的循环看起来像:

while(!done)
{
    cond_var.wait(std::chrono::milliseconds(20));

    // process messages...
    msg = dequeue_message();


    if(msg == done_message)
        done = true;
    else
        process_message(msg);

}

在另一个帖子中......

send_message(string msg)
{
    enqueue_message(msg);
    cond_var.release();
}        

如果时间空闲,等待条件变量,您的消息处理循环将花费最多。当发送消息并且发送线程释放条件变量时,接收线程将立即响应。

这允许您的接收线程以等待时间设置的最小速率循环,并由发送线程确定最大额定值。

答案 3 :(得分:1)

您要问的是如何正确实施Event Loop。使用OS调用。您向操作系统询问事件或消息。如果不存在消息,则OS仅将进程发送到休眠状态。在微控制器环境中,您可能没有操作系统。在那里必须使用interrupts的概念,这几乎是一个&#34;消息&#34; (或事件)在较低级别。

对于微控制器,您不会有睡眠或中断等概念,因此您只需循环播放即可。

在您的示例中,正确实现的getEvent()应该阻止并且在实际发生某些事情之前不执行任何操作,例如按键。

答案 4 :(得分:0)

确定这一点的最佳方法是自己测量它。

未延迟的循环将导致应用程序运行的特定核心100%使用。使用延迟语句,它将在0 - 1%左右。 (依靠getEvent函数的即时响应)

答案 5 :(得分:0)

嗯,这取决于几个因素 - 如果你不需要并行运行除了那个循环之外的任何其他东西,显然它没有性能差异。 但是可能出现的问题是功耗 - 取决于此环路的长度,您可以节省第二种类型中微控制器消耗的功率的90%。 把它称为一种不好的做法对我来说似乎并不合适 - 它适用于很多场景。

答案 6 :(得分:0)

正如我所知道的while循环,这个过程仍然保留在ram中。所以它不会让处理器在给定的延迟时使用它的资源。它在第二个代码中唯一的区别是在给定时间内执行while循环的次数。如果程序运行很长时间,这会有所帮助。否则第一种情况没问题。