前言:
??(不想看廢話的可以直接copy尾部的代碼)
??在八月上旬的時候获洲,曾經(jīng)寫過一個多線程爬蟲磨取。程序在運行時經(jīng)常莫名的卡死元潘。這令我很是費解畔乙,后來才發(fā)現(xiàn),是在請求對方資源時翩概,服務(wù)器長時間未返回完數(shù)據(jù)牲距。導(dǎo)致IO阻塞。
??其實不只是爬蟲钥庇,很多時候一個函數(shù)很可能因為某種不可預(yù)知的事情牍鞠,而有時很可能會卡在某一處,繼而函數(shù)無法繼續(xù)執(zhí)行下去上沐。導(dǎo)致?lián)砣?br>
??此時皮服,我們自然而然的會想到如果能寫一個裝飾器來限制一個函數(shù)執(zhí)行的時間,那么問題也就迎刃而解了参咙。
正文:
??最初,因為我的擁塞是發(fā)生在多進程中硫眯,我曾經(jīng)想通過殺死線程的方式來解決問題蕴侧。然而python中的threading模塊中并沒有提供這一方法。網(wǎng)上有類似方法两入,但是比較繁瑣净宵。并且非常不建議直接殺死線程(這其中可能關(guān)系到資源釋放的問題,比如我從隊列中取出了一個url裹纳, 然而我的進程卡死了择葡,并沒有完成相應(yīng)的任務(wù),我卻強行得終止了這個線程剃氧,url中的內(nèi)容未采集敏储,url也沒用放回到隊列中,豈不是造成了資源的浪費)朋鞍。后來想到可以直接對函數(shù)的運行時間進行限制已添,這樣就省了不少事情。
方法一:
??此方法比較容易理解滥酥,寫了一個裝飾器更舞,看到其中的__wrapper函數(shù),新建了一個守護線程坎吻,target是我們需要限制時間的函數(shù)缆蝉。啟動守護線程之后,主線程sleep(timer)(timer就是我們設(shè)定的函數(shù)運行時間)。若在規(guī)定的運行時間未結(jié)束守護進程(也就是我們需要限制運行時間的函數(shù))刊头,則主動拋出異常贝搁。(但此種方法有一個弊端,就是如果函數(shù)在目標(biāo)時間內(nèi)運行完了芽偏,由于sleep的原因雷逆,整個線程還是會等到sleep(timer)后結(jié)束,可能會浪費時間)
#!/usr/bin/env python
#-*- coding:utf-8 -*-
#Author: Chen
import threading
import time
def time_limited(timer):
'''
一個規(guī)定函數(shù)執(zhí)行時間的裝飾器
:param timer:
:return:
'''
def wrapper(func):
def __wrapper(params):
start_time = time.time()
#通過設(shè)置守護線程強制規(guī)定函數(shù)的運行時間
t = threading.Thread(target=func, args=params)
t.setDaemon(True)
t.start()
time.sleep(timer)
if t.is_alive():
#若在規(guī)定的運行時間未結(jié)束守護進程污尉,則主動拋出異常
raise Exception('Function execution timeout')
#print time.time()-start_time
return __wrapper
return wrapper
第二種:
??此種方法是第一種的升級膀哲,改進了函數(shù)運行完不結(jié)束的弊端
#!/usr/bin/env python
#-*- coding:utf-8 -*-
#Author: Chen
import time
from threading import Thread
ThreadStop = Thread._Thread__stop#獲取私有函數(shù)
def time_limited_pri(time_limited):
def wrapper(func):
def __wrapper(params):
class TimeLimited(Thread):
def __init__(self):
Thread.__init__(self)
def run(self):
func(params)
def _stop(self):
if self.is_alive():
ThreadStop(self)
raise Exception('Function execution overtime')
t = TimeLimited()
t.start()
t.join(timeout=time_limited)
if t.is_alive():
t._stop()
raise Exception('Function execution overtime')
return __wrapper
return wrapper