c中的定期回调

时间:2011-12-23 11:46:31

标签: c linux timer callback

我是一名新手,通过“alsa api”为声音播放编写代码。

我必须在一秒钟内重复调用函数send_output() 10次:

while(true)
{
     send_output();
     usleep(100000);
}

但这是一种糟糕的编程方式,因为在usleep()函数处理器中仍然忙于执行代码。

我想知道将函数send_output()放入周期性回调函数的方式(因为我知道它存在),以便在调用之间,处理器可以保持空闲以执行其他繁重的任务。你能救我吗?

P.S。我正在使用'Beagleboard'进行编程,它的时钟频率非常低,并且还需要执行其他任务。

感谢。

4 个答案:

答案 0 :(得分:1)

man usleep

   usleep - suspend execution for microsecond intervals

答案 1 :(得分:1)

您必须在multi-threads

的框架内执行此操作

根据你的标签,假设你是在Linux上编程,所以:

void thread_funtion()
{
    while(true)
    {
         send_output();
         usleep(100000);
    }
}

通过以下方式在主线程中调用此函数:

pthread_t pid;
pthread_create(&pid, NULL, thread_function, NULL);

请注意:

  1. #include <pthread.h>
  2. 如果您遇到gcc命令错误,请尝试gcc -pthread命令。

答案 2 :(得分:0)

很简单,你应该避免使用这样的同步。而是使用事件系统实现。定义一个接口并将其作为监听器注册到您的类。每当您的课程执行某些操作时,您将收到事件通知。您可以看一下Java监听器的实现,还可以阅读(Bridge,Strategy,Observer)设计模式。 c ++中的接口实现如下:

class IActionListener
{
public:
 virtual void actionPerformed( ... some arguments ... )=0;
};

实现这样的交互式界面:

class ConcreteActionListener: public IActionListener
{
public:
 void actionPerformed( ... some arguments ... )
 {
   // do your staff here.
 }
};

检查一下: http://en.wikipedia.org/wiki/Observer_pattern

答案 3 :(得分:0)

应用程序中需要协调多个事物的典型主循环以select函数为中心,该函数等待一组文件描述符上的事件或超时。

然后您的ALSA循环变为

// This will keep the time when ALSA expects the next packet
struct timeval alsa_timeout;

// Initialize timeout with current time
gettimeofday(&alsa_timeout, 0);

for(;;) {
    // Get current time
    struct timeval now;
    gettimeofday(&now, 0);

    // Whether to re-check the current time before going to sleep
    bool restart = false;

    if(now.tv_sec > alsa_timeout.tv_sec ||
            (now.tv_sec == alsa_timeout.tv_sec && now.tv_usec >= alsa_timeout.tv_usec)) {
        send_output();
        alsa_timeout.tv_usec += 100000;
        if(alsa_timeout.tv_usec > 1000000) {
            alsa_timeout.tv_sec += alsa_timeout.tv_usec / 1000000;
            alsa_timeout.tv_usec %= 1000000;
        }
        restart = true;
    }

    // If we performed some action, this would have taken time, so we need
    // to re-fetch the current time
    if(restart)
        continue;

    // Determine when the next action is due
    struct timeval next_timeout;

    // We only have one action at the moment, which is ALSA. Otherwise, this is the place
    // where you look for the earliest time you need to wake up.
    next_timeout.tv_sec = alsa_timeout.tv_sec;
    next_timeout.tv_usec = alsa_timeout.tv_usec;

    struct timeval relative_time;
    if(next_timeout.tv_usec >= now.tv_usec) {
        relative_time.tv_usec = next_timeout.tv_usec - now.tv_usec;
        relative_time.tv_sec = next_timeout.tv_sec - now.tv_sec;
    } else {
        relative_time.tv_usec = 1000000 - now.tv_usec + next_timeout.tv_usec;
        relative_time.tv_sec = next_timeout.tv_sec + 1 - now.tv_usec;
    }

    // Other parameters become important when you do other work
    int rc = select(0, 0, 0, 0, &relative_time);

    if(rc == 0)
        continue;
    if(rc == -1)
        break;

    // Handle file descriptor based work here
}

请注意,我在计算中使用绝对时间并为我发送的每个样本数据包递增 - 这使得数据速率保持不变,无论ALSA子系统将数据传输到声卡需要多长时间,以及何时错过超时(因为其他一些函数需要100多秒才能完成),输出函数将被调用两次以重新填充ALSA缓冲区。

您可能需要添加一些额外的代码:

  • 如果发生缓冲区欠载,请通过将alsa_timeout值重置为当前时间来重新启动,以便我们不会失去同步
  • 如果流运行很长时间,您可能需要稍微调整超时,因为声音芯片DAC时钟未与gettimeofday()后面的系统时钟同步
  • 如果调整系统时钟,则需要重新同步循环(您可以忽略夏令时,gettimeofday()返回UTC,但如果您运行NTP客户端或允许在某处设置日期和时间,则需要待处理)。