Python控制函数运行时间多线程

Python控制函数运行时间

 

在某个Flask项目在做后端接口时需要设置超时响应,因为接口中使用爬虫请求了多个网站,响应时间时长时短。

我需要设置一个最大响应时间,时间内如果接口爬虫没跑完,直接返回请求超时。

从网上了解到有两种方法,废话不多说直接上代码。

方法1:使用线程控制

复制代码
import requests, datetime, time
import threading


class MyThread(threading.Thread):
    def __init__(self, target, args=()):
        """
        why: 因为threading类没有返回值,因此在此处重新定义MyThread类,使线程拥有返回值
        此方法来源 https://www.cnblogs.com/hujq1029/p/7219163.html?utm_source=itdadao&utm_medium=referral
        """
        super(MyThread, self).__init__()
        self.func = target
        self.args = args

    def run(self):
        # 接受返回值
        self.result = self.func(*self.args)

    def get_result(self):
        # 线程不结束,返回值为None
        try:
            return self.result
        except Exception:
            return None


# 为了限制真实请求时间或函数执行时间的装饰器
def limit_decor(limit_time):
    """
    :param limit_time: 设置最大允许执行时长,单位:秒
    :return: 未超时返回被装饰函数返回值,超时则返回 None
    """

    def functions(func):
        # 执行操作
        def run(*params):
            thre_func = MyThread(target=func, args=params)
            # 主线程结束(超出时长),则线程方法结束
            thre_func.setDaemon(True)
            thre_func.start()
            # 计算分段沉睡次数
            sleep_num = int(limit_time // 1)
            sleep_nums = round(limit_time % 1, 1)
            # 多次短暂沉睡并尝试获取返回值
            for i in range(sleep_num):
                time.sleep(1)
                infor = thre_func.get_result()
                if infor:
                    return infor
            time.sleep(sleep_nums)
            # 最终返回值(不论线程是否已结束)
            if thre_func.get_result():
                return thre_func.get_result()
            else:
                return"请求超时"  #超时返回  可以自定义

        return run

    return functions

#接口函数
def a1():
    print("开始请求接口")

    #这里把逻辑封装成一个函数,使用线程调用
    a_theadiing = MyThread(target=a2)
    a_theadiing.start()
    a_theadiing.join()

    #返回结果
    a = a_theadiing.get_result()

    print("请求完成")
    return a
@limit_decor(3)   #超时设置为3s   2s逻辑未执行完毕返回接口超时
def a2():
    print("开始执行")
    time.sleep(2)
    print("执行完成")
    a=2
    return a

# 程序入口     未超时返回a的值   超时返回请求超时
if __name__ == '__main__':
    a = a1()  #调用接口(这里把函数a1看做一个接口)
    print(a)
复制代码

超时设置3s,线程调用函数运行2s,这里返回a的值2。

 

方法2:使用信号模块signal(只能在unix系统使用)

signal负责在Python程序内部处理信号,典型的操作包括预设信号处理函数,暂停并等待信号,以及定时发出SIGALRM等。

要注意,signal包主要是针对UNIX平台(比如Linux, MAC OS),而Windows内核中由于对信号机制的支持不充分,所以在Windows上的Python不能发挥信号系统的功能。

信号是进程之间通讯的方式,是一种软件中断。一个进程一旦接收到信号就会打断原来的程序执行流程来处理信号。

复制代码
def set_timeout(num):
    def wrap(func):
        def handle(signum, frame):  # 收到信号 SIGALRM 后的回调函数,第一个参数是信号的数字,第二个参数是the interrupted stack frame.
            raise RuntimeError

        def to_do(*args):
            try:
                signal.signal(signal.SIGALRM, handle)  # 设置信号和回调函数
                signal.alarm(num)  # 设置 num 秒的闹钟
                print('start alarm signal.')
                r = func(*args)
                print('close alarm signal.')
                signal.alarm(0)  # 关闭闹钟
                return r
            except RuntimeError as e:
                return "超时啦"
        return to_do

    return wrap


@set_timeout(2)  # 限时 2 秒超时
def connect():  # 要执行的函数
    time.sleep(3)  # 函数执行时间,写大于2的值,可测试超时
    return "完成"


if __name__ == '__main__':
    a = connect()
复制代码

 

 
分类: PythonPythonWeb
 
 
 

讲述了Python实现可设置持续运行时间、线程数及时间间隔的多线程异步post请求功能。分享给大家供大家参考,具体如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#coding=utf8
'''
random.randint(a, b):用于生成一个指定范围内的整数。
其中参数a是下限,参数b是上限,生成的随机数n: a <= n <= b
random.choice(sequence):从序列中获取一个随机元素
参数sequence表示一个有序类型(列表,元组,字符串)
'''
import httplib,json
import time
import threading
from random import randint,choice
#创建请求函数
def postRequest(threadNum):
  postJson={
        }
  #定义需要进行发送的数据
  postData=json.dumps(postJson)
  #定义一些文件头
  headerdata = {
    "content-type":"application/json",
     }
  #接口
  requrl ="/v1/query"
  #请求服务,例如:www.baidu.com
  hostServer=""
  #连接服务器
  conn = httplib.HTTPConnection(hostServer)
  #发送请求
  conn.request(method="POST",url=requrl,body=postData,headers=headerdata)
  #获取请求响应
  response=conn.getresponse()
  #打印请求状态
  if response.status in range(200,300):
    print u"线程"+str(threadNum)+u"状态码:"+str(response.status)
  conn.close()
def run(threadNum,internTime,duration):
  #创建数组存放线程
  threads=[]
  try:
    #创建线程
    for i in range(1,threadNum):
      #针对函数创建线程
      t=threading.Thread(target=postRequest,args=(i,))
      #把创建的线程加入线程组
      threads.append(t)
  except Exception,e:
    print e
  try:
    #启动线程
    for thread in threads:
        thread.setDaemon(True)
        thread.start()
        time.sleep(internTime)
    #等待所有线程结束
    for thread in threads:
        thread.join(duration)
  except Exception,e:
      print e
if __name__ == '__main__':
  startime=time.strftime("%Y%m%d%H%M%S")
  now=time.strftime("%Y%m%d%H%M%S")
  duratiion=raw_input(u"输入持续运行时间:")
  while (startime+str(duratiion))!=now:
    run(10,1,int(duratiion))
    now=time.strftime("%Y%m%d%H%M%S")
 
 
 

 

 

 

 

例讲述了python使用装饰器和线程限制函数执行时间的方法。分享给大家供大家参考。具体分析如下:

 

很多时候函数内部包含了一些不可预知的事情,比如调用其它软件,从网络抓取信息,可能某个函数会卡在某个地方不动态,这段代码可以用来限制函数的执行时间,只需要在函数的上方添加一个装饰器,timelimited(2)就可以限定函数必须在2秒内执行完成,如果执行完成则返回函数正常的返回值,如果执行超时则会抛出错误信息。

  1. # -*- coding: utf-8 -*-
  2. from threading import Thread
  3. import time
  4. class TimeoutException(Exception):
  5. pass
  6. ThreadStop = Thread._Thread__stop#获取私有函数
  7. def timelimited(timeout):
  8. def decorator(function):
  9. def decorator2(*args,**kwargs):
  10. class TimeLimited(Thread):
  11. def __init__(self,_error= None,):
  12. Thread.__init__(self)
  13. self._error = _error
  14. def run(self):
  15. try:
  16. self.result = function(*args,**kwargs)
  17. except Exception,e:
  18. self._error =e
  19. def _stop(self):
  20. if self.isAlive():
  21. ThreadStop(self)
  22. t = TimeLimited()
  23. t.start()
  24. t.join(timeout)
  25. if isinstance(t._error,TimeoutException):
  26. t._stop()
  27. raise TimeoutException('timeout for %s' % (repr(function)))
  28. if t.isAlive():
  29. t._stop()
  30. raise TimeoutException('timeout for %s' % (repr(function)))
  31. if t._error is None:
  32. return t.result
  33. return decorator2
  34. return decorator
  35. @timelimited(2)
  36. def fn_1(secs):
  37. time.sleep(secs)
  38. return 'Finished'
  39. if __name__ == "__main__":
  40. print fn_1(4)

 

希望本文所述对大家的Python程序设计有所帮助。

 

大家介绍了关于Python 2.x如何设置命令执行超时时间的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考借鉴,下面来一起看看吧。

前言

在Python2.x中的几个用来执行命令行的库或函数在执行命令是均不能设置一个命令执行的超时时间,用来在命令执行时间超时时终端这个命令的执行,这个功能在3.x(?)中解决了,但是在2.x还是只能自己实现。下面话不多说了,来一起看看详细的介绍吧。

下面就简单实现了一个版本:

  1. import subprocess
  2. from threading import Timer
  3.  
  4.  
  5. def call(args, timeout):
  6. = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  7.  
  8. timer = Timer(timeout, lambda process: process.kill(), [p])
  9.  
  10. try:
  11. timer.start()
  12. stdout, stderr = p.communicate()
  13. return_code = p.returncode
  14. return (stdout, stderr, return_code)
  15. finally:
  16. timer.cancel()

测试

  1. print call(['hostname'], 2)
  2. print call(['ping', 'www.baidu.com'], 2)

 

python程序运行超过时长强制退出方式,防止程序卡死;
主要两种方式:
1、程序内部设置时长,超过退出

import datetime
import time

import datetime
starttime = datetime.datetime.now()
#long running
endtime = datetime.datetime.now()
print (endtime – starttime).seconds

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
import datetime
import time


t1 = time.time()
t1 = time.localtime(t1).tm_hour
print(t1)
while 1:
    if time.localtime(time.time()).tm_hour - t1<3:
        print("@@@@",time.localtime(time.time()).tm_hour)
    else:
        print("break")
        break
print("finsh")


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
import time
import eventlet#导入eventlet这个模块
eventlet.monkey_patch()#必须加这条代码
with eventlet.Timeout(2,False):#设置超时时间为2秒
   time.sleep(4)
   print('没有跳过这条输出')
print('跳过了输出')

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

2、程序外部控制,超过强制退出

通过jenkins插件build-timeout ,安装后重启生效https://updates.jenkins.io/latest/build-timeout.hpi

Absolute

多少分钟没有结束则执行动作

参考:https://blog.csdn.net/juewuer/article/details/103469640


基于 signal模块实现:

signal包负责在Python程序内部处理信号,典型的操作包括预设信号处理函数,暂 停并等待信号,以及定时发出SIGALRM等。要注意,signal包主要是针对UNIX平台(比如Linux, MAC OS),而Windows内核中由于对信号机制的支持不充分,所以在Windows上的Python不能发挥信号系统的功能。 

  1.  
     
  2.  
     
  3.  
    # coding:utf8
  4.  
    import time
  5.  
    import signal
  6.  
     
  7.  
     
  8.  
    # 自定义超时异常
  9.  
    class TimeoutError(Exception):
  10.  
    def __init__(self, msg):
  11.  
    super(TimeoutError, self).__init__()
  12.  
    self.msg = msg
  13.  
     
  14.  
     
  15.  
    def time_out(interval, callback):
  16.  
    def decorator(func):
  17.  
    def handler(signum, frame):
  18.  
    raise TimeoutError("run func timeout")
  19.  
     
  20.  
    def wrapper(*args, **kwargs):
  21.  
    try:
  22.  
    signal.signal(signal.SIGALRM, handler)
  23.  
    signal.alarm(interval) # interval秒后向进程发送SIGALRM信号
  24.  
    result = func(*args, **kwargs)
  25.  
    signal.alarm(0) # 函数在规定时间执行完后关闭alarm闹钟
  26.  
    return result
  27.  
    except TimeoutError, e:
  28.  
    callback(e)
  29.  
    return wrapper
  30.  
    return decorator
  31.  
     
  32.  
     
  33.  
    def timeout_callback(e):
  34.  
    print(e.msg)
  35.  
     
  36.  
     
  37.  
    @time_out(2, timeout_callback)
  38.  
    def task1():
  39.  
    print("task1 start")
  40.  
    time.sleep(3)
  41.  
    print("task1 end")
  42.  
     
  43.  
     
  44.  
    @time_out(2, timeout_callback)
  45.  
    def task2():
  46.  
    print("task2 start")
  47.  
    time.sleep(1)
  48.  
    print("task2 end")
  49.  
     
  50.  
     
  51.  
    if __name__ == "__main__":
  52.  
    task1()
  53.  
    task2()

输出: 

  1.  
    task1 start
  2.  
    run func timeout
  3.  
    task2 start
  4.  
    task2 end

 基于子线程阻塞实现超时:

  1.  
    # coding:utf8
  2.  
    import time
  3.  
    import threading
  4.  
     
  5.  
     
  6.  
    def callback_func():
  7.  
    print('超时回调')
  8.  
     
  9.  
     
  10.  
    def time_out(interval, callback=None):
  11.  
    def decorator(func):
  12.  
    def wrapper(*args, **kwargs):
  13.  
    t =threading.Thread(target=func, args=args, kwargs=kwargs)
  14.  
    t.setDaemon(True) # 设置主线程技术子线程立刻结束
  15.  
    t.start()
  16.  
    t.join(interval) # 主线程阻塞等待interval秒
  17.  
    if t.is_alive() and callback:
  18.  
    return threading.Timer(0, callback).start() # 立即执行回调函数
  19.  
    else:
  20.  
    return
  21.  
    return wrapper
  22.  
    return decorator
  23.  
     
  24.  
     
  25.  
    @time_out(2, callback_func)
  26.  
    def task3(hh):
  27.  
    print('**********task3****************')
  28.  
    for i in range(3):
  29.  
    time.sleep(1)
  30.  
    print(i)
  31.  
    print(hh)
  32.  
     
  33.  
     
  34.  
    @time_out(2, callback_func)
  35.  
    def task4(hh):
  36.  
    print('**********task4****************')
  37.  
    for i in range(3):
  38.  
    # time.sleep(1)
  39.  
    print(i)
  40.  
    print(hh)
  41.  
     
  42.  
     
  43.  
    if __name__ == '__main__':
  44.  
    task3('参数')
  45.  
    task4('参数')

输出:

  1.  
    **********task3****************
  2.  
    0
  3.  
    参数
  4.  
    1
  5.  
    参数
  6.  
    超时回调
  7.  
    **********task4****************
  8.  
    0
  9.  
    参数
  10.  
    1
  11.  
    参数
  12.  
    2
  13.  
    参数

基于协程实现

  1.  
    def callback_func():
  2.  
    print('callback')
  3.  
     
  4.  
     
  5.  
    def time_out(interval, callback=None):
  6.  
    def decorator(func):
  7.  
    def wrapper(*args, **kwargs):
  8.  
    ########## 该部分必选在requests之前导入
  9.  
    import gevent
  10.  
    from gevent import monkey
  11.  
    monkey.patch_all()
  12.  
    ##########
  13.  
     
  14.  
    try:
  15.  
    gevent.with_timeout(interval, func, *args, **kwargs)
  16.  
    except gevent.timeout.Timeout as e:
  17.  
    callback() if callback else None
  18.  
     
  19.  
    return wrapper
  20.  
     
  21.  
    return decorator
  22.  
     
  23.  
     
  24.  
    @time_out(3, callback_func)
  25.  
    def func(a, b):
  26.  
    import time
  27.  
    time.sleep(2)
  28.  
    print(a,b)
  29.  
     
  30.  
     
  31.  
    func(1, 2)

 

 

 

中调用第三方接口时候,经常会出现请求超时的情况,或者参数的问题导致调用异代码异常。针对超时异常,查询了python 相关文档,没有并发现完善的包来根据用户自定义
的时间来抛出超时异常的模块。所以自己干脆自己来实现一个自定义的超时异常。目前找到了两种方式来实现超时异常的功能(signal.alarm()、threading实现超时异常)
方法1 thread + time 
原理:将要调用的功能函数放入子线程,通过设定子线程的阻塞时间,超时则主线程并不会等待子线程的执行。主线程退出,子线程就不存在了。
核心就是在程序中添加 join()方法,用于等待线程结束。join()的作用是,在子线程完成运行之前,这个子线程的父线程将会被一直阻塞.
 1 # coding=utf-8
 2 import threading
 3 import time
 4 
 5 
 6 def myFunc():
 7     time.sleep(4)
 8     print("myFunc执行了")
 9 
10 
11 if __name__ == '__main__':
12     t = threading.Thread(target=myFunc)
13     t.setDaemon(True)
14     t.start()
15 
16     t.join(2)
17     print("it's over")

执行结果:

  it's over

可以看出,当主线程执行到2秒时候,结束退出。子线程还没有结束,没有执行完及被强制退出

 1 # coding=utf-8
 2 import threading
 3 import time
 4 
 5 
 6 def myFunc():
 7     time.sleep(1)
 8     print("myFunc执行了")
 9 
10 
11 if __name__ == '__main__':
12     t = threading.Thread(target=myFunc)
13     t.setDaemon(True)
14     t.start()
15 
16     t.join(2)
17     print("it's over")
显示结果:

  myFunc执行了
  it's over

可以看出,子线程结束时,用时1秒,没有超过主线程设定的3秒,所以主线程与子线程都被执行了

方法 2  signal.alarm() ,注意两点:一是signal信号机制要在linux上才能运行; 二是signal信号在主线程中才会会起作用

 1 import signal
 2 import time
 3 
 4 
 5 # Define signal handler function
 6 def myHandler(signum, frame):
 7     exit("TimeoutError")
 8 
 9 
10 def test_fun():
11     # time.sleep(3)
12     int("afsdf")
13     a = 2 + 3
14 
15     return a
16 
17 
18 
19 if __name__ == '__main__':
20     try:
21         signal.signal(signal.SIGALRM, myHandler)
22         signal.alarm(2)
23         test = test_fun()
24         print(test)
25         signal.alarm(0)
26     except Exception as ret:
27         print("msg:", ret)

执行结果:
  当 time.sleep(3) 时,会抛出TimeoutError的异常
  当 test_fun 里面出现 int("afsdf")时, 会抛出 ValueError("invalid literal for int() with base 10: 'afsdf'",))
  当test_fun函数执行的时间小于2 秒时,就会返回函数对应的值

方法3  带有返回值的超时异常,可以通过创建thread类的方式来进行捕捉

 1 import threading
 2 import sys
 3 import time
 4 
 5 
 6 class Dispacher(threading.Thread):
 7     def __init__(self, fun, args):
 8         threading.Thread.__init__(self)
 9         self.setDaemon(True)
10         self.result = None
11         self.error = None
12         self.fun = fun
13         self.args = args
14 
15         self.start()
16 
17     def run(self):
18         try:
19             self.result = self.fun(self.args)
20         except:
21             self.error = sys.exc_info()
22 
23 
24 def test_fun(i):
25     # time.sleep(4)
26     a = i*i
27     # b    
29   return a
30 def main_fun():
31     c = Dispacher(test_fun, 2)
32     c.join(2)
33 
34     if c.isAlive():
35         return "TimeOutError"
36     elif c.error:
37         return c.error[1]
38     t = c.result
39     return t
40 
41 if __name__ == '__main__':
42     fun = main_fun()
43     print(fun)
     
显示结果:
  test_fun 执行时间大于设置的2秒时,会抛出TimeOutError
  test_fun 执行时间小于设置的2秒时,并且函数正常执行时,显示:4
  test_fun 里面出现比如 “b” 时,会抛出 global name 'b' is not defined 的异常


 

 

posted on 2020-11-09 22:43  shuzihua  阅读(3818)  评论(0编辑  收藏  举报

导航