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()
講述了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秒內執行完成,如果執行完成則返回函數正常的返回值,如果執行超時則會拋出錯誤信息。
- # -*- coding: utf-8 -*-
- from threading import Thread
- import time
- class TimeoutException(Exception):
- pass
- ThreadStop = Thread._Thread__stop#獲取私有函數
- def timelimited(timeout):
- def decorator(function):
- def decorator2(*args,**kwargs):
- class TimeLimited(Thread):
- def __init__(self,_error= None,):
- Thread.__init__(self)
- self._error = _error
- def run(self):
- try:
- self.result = function(*args,**kwargs)
- except Exception,e:
- self._error =e
- def _stop(self):
- if self.isAlive():
- ThreadStop(self)
- t = TimeLimited()
- t.start()
- t.join(timeout)
- if isinstance(t._error,TimeoutException):
- t._stop()
- raise TimeoutException('timeout for %s' % (repr(function)))
- if t.isAlive():
- t._stop()
- raise TimeoutException('timeout for %s' % (repr(function)))
- if t._error is None:
- return t.result
- return decorator2
- return decorator
- @timelimited(2)
- def fn_1(secs):
- time.sleep(secs)
- return 'Finished'
- if __name__ == "__main__":
- print fn_1(4)
希望本文所述對大家的Python程序設計有所幫助。
大家介紹了關於Python 2.x如何設置命令執行超時時間的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考借鑒,下面來一起看看吧。
前言
在Python2.x中的幾個用來執行命令行的庫或函數在執行命令是均不能設置一個命令執行的超時時間,用來在命令執行時間超時時終端這個命令的執行,這個功能在3.x(?)中解決了,但是在2.x還是只能自己實現。下面話不多說了,來一起看看詳細的介紹吧。
下面就簡單實現了一個版本:
- import subprocess
- from threading import Timer
- def call(args, timeout):
- p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- timer = Timer(timeout, lambda process: process.kill(), [p])
- try:
- timer.start()
- stdout, stderr = p.communicate()
- return_code = p.returncode
- return (stdout, stderr, return_code)
- finally:
- timer.cancel()
測試
- print call(['hostname'], 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不能發揮信號系統的功能。
-
-
-
# coding:utf8
-
import time
-
import signal
-
-
-
# 自定義超時異常
-
class TimeoutError(Exception):
-
def __init__(self, msg):
-
super(TimeoutError, self).__init__()
-
self.msg = msg
-
-
-
def time_out(interval, callback):
-
def decorator(func):
-
def handler(signum, frame):
-
raise TimeoutError("run func timeout")
-
-
def wrapper(*args, **kwargs):
-
try:
-
signal.signal(signal.SIGALRM, handler)
-
signal.alarm(interval) # interval秒后向進程發送SIGALRM信號
-
result = func(*args, **kwargs)
-
signal.alarm( 0) # 函數在規定時間執行完后關閉alarm鬧鍾
-
return result
-
except TimeoutError, e:
-
callback(e)
-
return wrapper
-
return decorator
-
-
-
def timeout_callback(e):
-
print(e.msg)
-
-
-
-
def task1():
-
print( "task1 start")
-
time.sleep( 3)
-
print( "task1 end")
-
-
-
-
def task2():
-
print( "task2 start")
-
time.sleep( 1)
-
print( "task2 end")
-
-
-
if __name__ == "__main__":
-
task1()
-
task2()
輸出:
-
task1 start
-
run func timeout
-
task2 start
-
task2 end
基於子線程阻塞實現超時:
-
# coding:utf8
-
import time
-
import threading
-
-
-
def callback_func():
-
print( '超時回調')
-
-
-
def time_out(interval, callback=None):
-
def decorator(func):
-
def wrapper(*args, **kwargs):
-
t =threading.Thread(target=func, args=args, kwargs=kwargs)
-
t.setDaemon( True) # 設置主線程技術子線程立刻結束
-
t.start()
-
t.join(interval) # 主線程阻塞等待interval秒
-
if t.is_alive() and callback:
-
return threading.Timer(0, callback).start() # 立即執行回調函數
-
else:
-
return
-
return wrapper
-
return decorator
-
-
-
-
def task3(hh):
-
print( '**********task3****************')
-
for i in range(3):
-
time.sleep( 1)
-
print(i)
-
print(hh)
-
-
-
-
def task4(hh):
-
print( '**********task4****************')
-
for i in range(3):
-
# time.sleep(1)
-
print(i)
-
print(hh)
-
-
-
if __name__ == '__main__':
-
task3( '參數')
-
task4( '參數')
輸出:
-
**********task3****************
-
0
-
參數
-
1
-
參數
-
超時回調
-
**********task4****************
-
0
-
參數
-
1
-
參數
-
2
-
參數
基於協程實現
-
def callback_func():
-
print( 'callback')
-
-
-
def time_out(interval, callback=None):
-
def decorator(func):
-
def wrapper(*args, **kwargs):
-
########## 該部分必選在requests之前導入
-
import gevent
-
from gevent import monkey
-
monkey.patch_all()
-
##########
-
-
try:
-
gevent.with_timeout(interval, func, *args, **kwargs)
-
except gevent.timeout.Timeout as e:
-
callback() if callback else None
-
-
return wrapper
-
-
return decorator
-
-
-
-
def func(a, b):
-
import time
-
time.sleep( 2)
-
print(a,b)
-
-
-
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 的異常