เรียกใช้โค้ดการล้างข้อมูลได้อย่างน่าเชื่อถือเมื่อสิ้นสุดโปรแกรม
ขณะนี้มีโมดูลบิวท์อินสองโมดูลสำหรับจัดการพฤติกรรมการสิ้นสุดใน Python: atexit
และ signal
อย่างไรก็ตาม การใช้พวกมันโดยตรงทำให้เกิดโค้ดสำเร็จรูปซ้ำๆ มากมาย และพฤติกรรมบางอย่างที่ไม่ชัดเจนซึ่งอาจผิดพลาดได้ง่ายโดยไม่ตั้งใจ ซึ่งเป็นเหตุผลว่าทำไมฉันถึงเขียนแพ็คเกจนี้
ขณะนี้โมดูล atexit
ไม่เพียงพอเนื่องจากไม่สามารถจัดการสัญญาณได้ ขณะนี้โมดูล signal
ไม่เพียงพอเนื่องจากไม่สามารถจัดการทางออกปกติหรือที่เกิดจากข้อยกเว้นได้
วิธีการทั่วไปจะรวมถึงโค้ดที่ทำซ้ำบ่อยครั้งเพื่อลงทะเบียนฟังก์ชันทั้งที่มี atexit
และสัญญาณที่ต้องการ อย่างไรก็ตาม บางครั้งจำเป็นต้องมีการดูแลเป็นพิเศษเพื่อให้แน่ใจว่าฟังก์ชันจะไม่ทำงานสองครั้ง (หรือเป็นค่าเดิม) และตัวจัดการสัญญาณที่ลงทะเบียนไว้ก่อนหน้านี้จะถูกเรียก
แพคเกจนี้ทำหรืออนุญาตการทำงานดังต่อไปนี้:
ลงทะเบียนฟังก์ชันที่จะเรียกใช้เมื่อสิ้นสุดโปรแกรม
@ pyterminate .register
@ pyterminate .register(signals=(signal.SIGINT, signal.SIGABRT))
อนุญาตให้ลงทะเบียนหลายฟังก์ชันได้
จะเรียกตัวจัดการสัญญาณที่ลงทะเบียนไว้ก่อนหน้านี้
อนุญาตให้ใช้รหัสทางออกที่เป็นศูนย์หรือไม่เป็นศูนย์บนสัญญาณที่จับได้:
@ pyterminate .register(successful_exit=True)
อนุญาตให้ระงับหรือขว้าง KeyboardInterrupt
บน SIGINT
:
@ pyterminate .register(keyboard_interrupt_on_sigint=True)
KeyboardInterrupt
หากมีการกำหนดการจัดการข้อยกเว้นเพิ่มเติม อนุญาตให้ฟังก์ชั่นยกเลิกการลงทะเบียน: pyterminate .unregister(func)
ละเว้นสัญญาณที่ร้องขอในขณะที่ฟังก์ชันที่ลงทะเบียนไว้กำลังดำเนินการ เพื่อให้แน่ใจว่าจะไม่ถูกรบกวน
SIGKILL
และการเรียกไปยัง os._exit()
ไม่สามารถละเลยได้ python3 -m pip install pyterminate
import signal
import pyterminate
@ pyterminate . register (
args = ( None ,),
kwargs = { "b" : 42 },
signals = ( signal . SIGINT , signal . SIGTERM ),
successful_exit = True ,
keyboard_interrupt_on_sigint = True
)
def cleanup ( * args , ** kwargs ):
...
# or
pyterminate . register ( cleanup , ...)
ตั้งแต่สร้างกระบวนการใหม่ผ่านการฟอร์กซ้ำกระบวนการทั้งหมด ฟังก์ชันใดๆ ที่ลงทะเบียนไว้ก่อนหน้านี้ก็จะถูกลงทะเบียนในกระบวนการฟอร์คด้วย นี่เป็นผลลัพธ์ที่ชัดเจนของการฟอร์ก แต่สิ่งสำคัญที่ต้องพิจารณาว่าฟังก์ชันที่ลงทะเบียนไว้กำลังเข้าถึงทรัพยากรที่ใช้ร่วมกันหรือไม่ เพื่อหลีกเลี่ยงพฤติกรรมนี้ คุณสามารถยกเลิกการลงทะเบียนฟังก์ชันที่จุดเริ่มต้นของกระบวนการที่แยกออก เกทตาม ID ของกระบวนการ หรือใช้วิธีการซิงโครไนซ์อื่น ๆ ที่เหมาะสม
เมื่อเริ่มต้นกระบวนการด้วยโมดูล multiprocessing
ของ Python วิธี fork
จะล้มเหลวในการเรียกใช้ฟังก์ชันที่ลงทะเบียนไว้เมื่อออก เนื่องจากกระบวนการสิ้นสุดด้วย os._exit()
ภายใน ซึ่งจะข้ามการล้างข้อมูลทั้งหมดและหยุดกระบวนการทันที
วิธีหนึ่งในการหลีกเลี่ยงปัญหานี้คือการใช้วิธีการเริ่มต้น "spawn"
หากนั่นเป็นที่ยอมรับสำหรับแอปพลิเคชันของคุณ อีกวิธีหนึ่งคือการลงทะเบียนฟังก์ชันของคุณกับสัญญาณที่ผู้ใช้กำหนด และล้อมโค้ดกระบวนการของคุณในบล็อก try-ยกเว้น เพื่อเพิ่มสัญญาณที่ผู้ใช้กำหนดในตอนท้าย pyterminate
จัดเตรียมฟังก์ชันนี้ในรูปแบบของ exit_with_signal
มัณฑนากร ซึ่งเพียงแค่ล้อมฟังก์ชันที่ได้รับการตกแต่งไว้ในบล็อกแบบลองสุดท้าย และเพิ่มสัญญาณที่กำหนด ตัวอย่างการใช้งาน:
import multiprocessing as mp
import signal
import pyterminate
@ pyterminate . exit_with_signal ( signal . SIGUSR1 )
def run_process ():
@ pyterminate . register ( signals = [ signal . SIGUSR1 , signal . SIGINT , signal . SIGTERM ])
def cleanup ():
...
...
if __name__ == "__main__"
mp . set_start_method ( "fork" )
proc = mp . Process ( target = run_process )
proc . start ()
try :
proc . join ( timeout = 300 )
except TimeoutError :
proc . terminate ()
proc . join ()