【问题标题】:Pass a function if it takes more than 5 seconds如果需要超过 5 秒,则传递一个函数
【发布时间】:2021-04-24 19:25:27
【问题描述】:

我在 for 循环中调用了一个函数,但是我想检查该函数的执行时间是否超过 5 秒,我想传递该迭代并继续进行下一次迭代。

我考虑过使用时间库,并启动一个时钟,但结束计时器只会在函数执行后执行,因此我无法在 5 秒内通过该特定迭代

【问题讨论】:

  • 那些确实需要超过5秒才能继续运行直到完成的函数可以吗?
  • @quamrana 不,这正是我想要避免的
  • 所以你的意思是问:'如果一个函数仍在运行,是否可以停止它以便我可以调用其他东西?'
  • @quamrana 这是以不同值运行的同一个函数,在 for 循环中执行。我想看看它的执行是否需要超过 5 秒的特定值然后移动到同一函数的下一次迭代

标签: python python-3.x time


【解决方案1】:

我将使用 subprocess 模块发布替代解决方案。您需要使用您的函数创建一个 python 文件,将其作为子进程调用,然后调用 wait 方法。如果进程无法在期望的时间内完成,则会引发错误,因此您终止该进程并继续迭代。

例如,这是您要调用的函数:

from time import time
import sys

x = eval(sys.argv[1])   

t = time()
a = [i for i in range(int(x**5))]

#pipe to the main process the computaiton time
sys.stdout.write('%s'%(time()-t))

还有main函数,这里我在func.py文件中调用了前一个函数:

import subprocess as sp
from subprocess import Popen, PIPE


for i in range(1,50,1):
    #call the process
    process = Popen(['python','~func.py', '%i'%i],
                    stdout = PIPE,stdin = PIPE)

    try:
        #if it finish within 1 sec:
        process.wait(1)
        print('Finished in: %s s'%(process.stdout.read().decode()))

    except:
        #else kill the process. It is important to kill it,
        #otherwise it will keep running.
        print('Timeout')
        process.kill()
        

【讨论】:

    【解决方案2】:

    这是我一直在试验的一些代码,它有一个 task() 迭代它 params 参数并花费随机时间来完成每个参数。

    我为每个task 启动一个thread,通过监视返回值队列等待线程完成。如果线程未能完成,则主循环放弃它,并开始下一个线程。

    程序显示哪些任务失败或完成(每次都不同)。

    完成的任务会打印出它们的结果(参数和睡眠时间)。

    import threading, queue
    import random
    import time
    
    def task(params, q):
        for p in params:
            s = random.randint(1,4)
            s = s * s
            s = s / 8
            time.sleep(s)
            q.put((p,s), False)
        q.put(None, False)  # None is sentinal value
    
    def sampleQueue(q, ret, results):
        while not q.empty():
            item = q.get()
            if item:
                ret.append(item)
            else:
                # Found None sentinal
                results.append(ret)
                return True
        return False
        
    
    old = []
    results = []
    for p in [1,2,3,4]:
        q = queue.SimpleQueue()
        t = threading.Thread(target=task, args=([p,p,p,p,p], q))
        t.start()
        end = time.time() + 5
        ret = []
        failed = True
        while time.time() < end:
            time.sleep(0.1)
            if sampleQueue(q, ret, results):
                failed = False
                break
        if failed:
            print(f'Task {p} failed!')
            old.append(t)
        else:
            print(f'Task {p} finished!')
            t.join()
    
    print(results)
    print(f'{len(old)} threads failed')
    for t in old:
        t.join()
    print('Done')
    

    示例输出:

    Task 1 finished!
    Task 2 finished!
    Task 3 failed!
    Task 4 failed!
    [[(1, 1.125), (1, 1.125), (1, 2.0), (1, 0.125), (1, 0.5)], [(2, 0.125), (2, 1.125), (2, 0.5), (2, 2.0), (2, 0.125)]]
    2 threads failed
    Done
    

    【讨论】:

    • 这段代码中任务完成了吗?我的意思是你是否测量所花费的时间,然后如果它失败或完成了,或者如果时间太长,任务是否会被打断?
    • 我的立场是,如果一个给定的任务花费的时间太长,而且它主要是网络绑定的,那么忽略它就可以了。但是,是的,被忽略的任务仍在运行,并且以t.join() 结尾的循环等待所有失败的任务最终完成,以保持整洁。可以同时运行许多失败的任务这一事实应该不是问题,因为它们都是网络绑定的,并且实际上并不占用太多的 cpu 运行时间。
    【解决方案3】:

    我在下面附上一个例子。希望这对您有所帮助:

    from threading import Timer 
    class LoopStopper: 
     
        def __init__(self, seconds): 
            self._loop_stop = False 
            self._seconds = seconds 
      
        def _stop_loop(self): 
            self._loop_stop = True 
     
        def run( self, generator_expression, task): 
            """ Execute a task a number of times based on the generator_expression""" 
            t = Timer(self._seconds, self._stop_loop) 
            t.start() 
            for i in generator_expression: 
                task(i) 
                if self._loop_stop: 
                    break 
            t.cancel() # Cancel the timer if the loop ends ok. 
     
    ls = LoopStopper( 5) # 5 second timeout 
    ls.run( range(1000000), print) # print numbers from 0 to 999999
    

    【讨论】:

    • 感谢您的回答,您能否提供一个解释。那么这是否意味着如果我运行 ls.run( MyFunction) 它只会运行 5 秒,否则它会停止它?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多