【问题标题】:How to set a timeout for a function in C?如何为C中的函数设置超时?
【发布时间】:2011-12-06 00:23:12
【问题描述】:

我有一个要求,我必须给 xx ms 才能执行一个函数。在 xx ms 之后,我必须中止该功能。请帮助我如何在 C 中实现它。

【问题讨论】:

    标签: c


    【解决方案1】:

    我认为最好的方法是使用 pthreads。在自己的线程中启动可能需要取消的计算,并在主线程中使用pthread_cond_timedwait:

    #include <time.h>
    #include <pthread.h>
    #include <stdio.h>
    /* for ETIMEDOUT */
    #include <errno.h>
    #include <string.h>
    
    pthread_mutex_t calculating = PTHREAD_MUTEX_INITIALIZER;
    pthread_cond_t done = PTHREAD_COND_INITIALIZER;
    
    void *expensive_call(void *data)
    {
            int oldtype;
    
            /* allow the thread to be killed at any time */
            pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype);
    
            /* ... calculations and expensive io here, for example:
             * infinitely loop
             */
            for (;;) {}
    
            /* wake up the caller if we've completed in time */
            pthread_cond_signal(&done);
            return NULL;
    }
    
    /* note: this is not thread safe as it uses a global condition/mutex */
    int do_or_timeout(struct timespec *max_wait)
    {
            struct timespec abs_time;
            pthread_t tid;
            int err;
    
            pthread_mutex_lock(&calculating);
    
            /* pthread cond_timedwait expects an absolute time to wait until */
            clock_gettime(CLOCK_REALTIME, &abs_time);
            abs_time.tv_sec += max_wait->tv_sec;
            abs_time.tv_nsec += max_wait->tv_nsec;
    
            pthread_create(&tid, NULL, expensive_call, NULL);
    
            /* pthread_cond_timedwait can return spuriously: this should
             * be in a loop for production code
             */
            err = pthread_cond_timedwait(&done, &calculating, &abs_time);
    
            if (err == ETIMEDOUT)
                    fprintf(stderr, "%s: calculation timed out\n", __func__);
    
            if (!err)
                    pthread_mutex_unlock(&calculating);
    
            return err;
    }
    
    int main()
    {
            struct timespec max_wait;
    
            memset(&max_wait, 0, sizeof(max_wait));
    
            /* wait at most 2 seconds */
            max_wait.tv_sec = 2;
            do_or_timeout(&max_wait);
    
            return 0;
    }
    

    你可以在 linux 上编译和运行它:

    $ gcc test.c -pthread -lrt && ./a.out
    do_or_timeout: calculation timed out
    

    【讨论】:

    • 您的do_or_timeout 的结尾与我有关。如果我从我的主函数多次调用此函数并且在第一次调用时超时,它会恢复锁定吗?看起来只有在没有超时的情况下才会释放锁。
    • 我会提供一个指向 do_or_timeout 的指针信号,并且基于这个信号的 while 循环将在超时发生时由父线程控制,因此子线程可以有机会优雅地退出
    • 在将 max_wait 添加到 abs_time 之后添加将纳秒转换为秒的过程至关重要,因为在这种情况下,当前以纳秒为单位的时间戳足够大,可以在添加超时后整整一秒:while(abs_time.tv_nsec &gt;= 1000000000L) { abs_time.tv_sec += 1; abs_time.tv_nsec -= 1000000000L; } 如果您不这样做,即使在通常不会发生的情况下,有时您也会超时。
    【解决方案2】:
    ->include time.h 
    ->take two variable for start time & current time of type time_t
    like time_t start_time,current_time
    -> take start time 
       time(&start_time);
    now in while loop continuisly check for 
       time(&current_time)
       difftime(current_time,start_time)
    if difftime's return value is 15ms break while loop & close your program 
    

    【讨论】:

    • 如果你仍然没有得到我的提示,那么说我会给你完整的代码..当我有时间..!!
    • 我认为他想要的是确保一个函数在一定的时间范围内完成执行,而不是将这个时间逻辑放在函数本身中。
    • -1。这如何解决问题?如果你的 while 循环中有一个需要 30 分钟的调用,它不会停止执行,直到该函数返回。
    • @NoufalIbrahim devi 没有指定他想在那个函数中做点什么,所以我不在乎,但感谢你指出了那个错误..我需要重新考虑整个设计..! !这种情况下应该怎么解决?
    • 问题标题是“如何为函数设置超时”。我可能会使用看门狗线程和主线程来运行实际功能,类似于 Bobby Powers 建议的方式。
    【解决方案3】:

    如果您不使用 pthreads,您也可以使用 Apache Portable Runtime 执行类似的超时功能:http://apr.apache.org/docs/apr/1.4/group__apr__thread__proc.html

    #include <stdio.h>
    #include <stdlib.h>
    #include <stdbool.h>
    #include "apr.h"
    #include "apr_thread_proc.h"
    #include "apr_time.h"
    
    void *APR_THREAD_FUNC expensive_call(apr_thread_t *thread, void *data)
    {
        (void)thread;
        bool *done = data;
    
        /* ... calculations and expensive io here, for example:
         * infinitely loop
         */
        for (;;) {}
    
        // signal caller that we are done
        *done = true;
        return NULL;
    }
    
    bool do_or_timeout(apr_pool_t *pool, apr_thread_start_t func, int max_wait_sec)
    {
        apr_thread_t *thread;
        bool thread_done = false;
        apr_thread_create(&thread, NULL, func, &thread_done, pool);
        apr_time_t now = apr_time_now();
        for (;;) {
            if (thread_done) {
                apr_thread_join(NULL, thread);
                return true;
            }
            if (apr_time_now() >= now + apr_time_make(max_wait_sec, 0)) {
                return false;
            }
            // avoid hogging the CPU in this thread
            apr_sleep(10000);
        }
    }
    
    int main(void)
    {
        // initialize APR
        apr_initialize();
        apr_pool_t *ap;
        if (apr_pool_create(&ap, NULL) != APR_SUCCESS) {
            exit(127);
        }
    
        // try to do the expensive call; wait up to 3 seconds
        bool completed = do_or_timeout(ap, expensive_call, 3);
        if (completed) {
            printf("expensive_call completed\n");
        } else {
            printf("expensive_call timed out\n");
        }
    
        apr_terminate();
        return 0;
    }
    

    使用这样的命令编译

    gcc -o 示例 example.c -lapr-1

    【讨论】:

      【解决方案4】:

      我不知道那个架构,所以我只能给你一个一般的提示。我会尝试类似于旧 Symbian TRAP 机制的东西。

      1. 在主程序中:

        • 启动计时器。
        • 收起一个堆栈指针
        • 收起一个程序计数器。
        • 调用你的函数。
      2. 在定时器异常(中断)处理程序中。 这有点棘手,因为当异常处理开始时,您需要知道给定架构堆栈指针和程序计数器的保存位置(处理器的数据表)。程序计数器很可能被推送到主例程堆栈。 所以你的步骤是:

        • 用您复制的值替换堆栈指针值(用于主程序)。
        • 将程序计数器值替换为您复制的值 + 偏移量(因为您希望在函数调用后返回执行 - 最好检查汇编代码以确定它有多大)。
        • 从异常(中断)处理例程返回。

      【讨论】:

      • 根据 Renas V850 架构数据表 PC 存储在 EIPC 寄存器中。
      • 是的,但这是正确的,但前提是您不从该函数中调用任何其他函数。抱歉,我这辈子没见过 v850 系列和瑞萨编译器,所以我能帮到你的就这么多了。
      【解决方案5】:

      @Bobby Powers 的答案是可行的,但需要进行一些更改,如下所示

      if (!err)
          pthread_mutex_unlock(&calculating);
      -> change to 
          pthread_mutex_unlock(&calculating);
      as @T.D. Smith says
      
      and need add  
      pthread_cancel(tid) // if isn't add, the expensive_call may never exit if your function couldn't exit by itself, such as for (;;) {} or something block operation.  
      

      【讨论】:

        猜你喜欢
        • 2021-07-29
        • 2018-11-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-03-15
        • 2020-07-23
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多