簡體   English   中英

定時器阻止程序退出

[英]Timer preventing program from exiting

我正在編寫一個程序,它允許我使用 python 控制一個普通的 Minecraft 服務器。 我想做的第一件事是自動重啟功能。 一切正常,除了我不能做sys.exit()或類似的事情,我不確定,但我認為這是因為Timer

我試過t.cancel()t是一個局部變量,所以玩起來很復雜。

這是我的代碼:

#! /usr/bin/env python3
import os, sys, time
import subprocess
from datetime import datetime, timedelta, date
from threading import Timer

server = subprocess.Popen('./start.sh', stdin=subprocess.PIPE,shell=True)
#junk variables
content = ''
previousContent = ''

def restart():
    if datetime.today().weekday() is 3:
        server.stdin.write(bytes('stop\r\n', 'ascii'))
        server.stdin.flush()
        time.sleep(90)
        print('Restarting...')
        os.system('python3 start.py')
        sys.exit()
    else:
        timerStart()

def timerStart():
    today = datetime.today()
    restartDate = today.replace(day=today.day,hour=1,minute=0,second=0,microsecond=0) + timedelta(days=1)
    delta_t = restartDate-today
    secs= delta_t.total_seconds()
    t=Timer(secs, restart)
    t.start()

timerStart()

while True:
    time.sleep(0.1)
    
    #stdout
    f = open('logs/latest.log')
    content = f.read()
    if previousContent != '':
        if previousContent in content:
            content.replace(previousContent,'')
            if content != '':
                print(content)
    previousContent = f.read()
    f.close()

    #stdin
    command = input('')
    if command:
        if command == 'stop':
            server.stdin.write(bytes('stop\r\n', 'ascii'))
            server.stdin.flush()
            time.sleep(20)
            sys.exit()
        else:
            server.stdin.write(bytes(command + '\r\n', 'ascii'))
            server.stdin.flush()

如果有人至少能讓我走上正軌,那真的會幫助我

當實例化threading.Timer class,並在對應的object中調用其start()方法時,會創建一個新的非守護進程執行線程,並在后台啟動執行方法“restart”。 創建的執行線程,在實例化線程時等待“secs”變量中指定的時間間隔。Timer class 過去,然后執行指定的“restart”方法。 調用 sys.exit() 時腳本的執行沒有結束的原因是應用程序的主執行線程為了完成它的執行(以及腳本的執行),必須等待創建的所有其他非守護程序線程首先完成。 也就是說,當您調用進程的主執行線程 sys.exit() 時,必須等待由 threading.Timer class 實例化創建的線程才能完成其執行。 為了解決這個問題,正如您所提到的,您可以做的是調用 t.cancel()。 現在為此,您必須首先將變量“t”的聲明移動到全局 scope,以便能夠在“timerStart”function 和執行“停止”命令的代碼塊中使用它。 例如,在變量“previousContent”的聲明和初始化之后,您可以在此處聲明變量 t 並將其初始化為 None。 然后,在將 threading.Timer 實例分配給它之前,您必須使用“全局”關鍵字,該關鍵字允許修改當前范圍之外的變量(在“timerStart”函數的 scope 之外),最后,在處理“停止”命令,調用 t.cancel()。 簡而言之,最終代碼如下所示:

# ! /usr/bin/env python3
import os, sys, time
import subprocess
from datetime import datetime, timedelta, date
from threading import Timer

server = subprocess.Popen('./start.sh', stdin=subprocess.PIPE, shell=True)
# junk variables
content = ''
previousContent = ''
t = None


def restart():
    if datetime.today().weekday() is 3:
        server.stdin.write(bytes('stop\r\n', 'ascii'))
        server.stdin.flush()
        time.sleep(90)
        print('Restarting...')
        os.system('python3 start.py')
        sys.exit()
    else:
        timerStart()


def timerStart():
    today = datetime.today()
    restartDate = today.replace(day=today.day, hour=1, minute=0, second=0, microsecond=0) + timedelta(days=1)
    delta_t = restartDate - today
    secs = delta_t.total_seconds()
    global t
    t = Timer(secs, restart)
    t.start()


timerStart()

while True:
    time.sleep(0.1)

    # stdout
    with open('logs/latest.log') as f:
        # Is better to open the file using a "with", such that the file is guaranteed to be closed,
        # even in the case of an exception
        content = f.read()
        if previousContent != '':
            if previousContent in content:
                content.replace(previousContent, '')
                if content != '':
                    print(content)
        previousContent = f.read()

    # stdin
    command = input('')
    if command:
        if command == 'stop':
            # Abort the threading.Timer's non-daemon execution thread:
            t.cancel()
            # Blocks the calling thread until the thread whose join() method is called is terminated:
            t.join()
            server.stdin.write(bytes('stop\r\n', 'ascii'))
            server.stdin.flush()
            time.sleep(20)
            sys.exit()
        else:
            server.stdin.write(bytes(command + '\r\n', 'ascii'))
            server.stdin.flush()

另外,很高興知道,通過實例化 threading.Timer class 創建的線程只能在它開始執行“重啟”方法之前被取消。 class threading.Timer 繼承自 threading.Thread,這是它在調用 start() 時創建並觸發新的非守護程序執行線程執行的方式。

參考:

定時器對象線程對象

如果您對線程所做的唯一事情是重新啟動腳本,您可以嘗試一些更簡單的方法。 如果您為計時器中斷設置信號處理程序,然后啟動一個計時器,當您希望代碼重新啟動時將觸發該計時器,您可以跳過所有線程處理。

#!/usr/bin/env python3

import signal
...other imports...

def do_restart(signal_num, frame):
    server.stdin.write(bytes('stop\r\n', 'ascii'))
    server.stdin.flush()
    time.sleep(90)
    print('Restarting...')
    os.system('python3 start.py')
        sys.exit()

...calculate how long before the restart...

signal.signal(signal.SIGALRM, do_restart)
signal.alarm(seconds_until_restart)

while True:
    time.sleep(0.1)
    
    #stdout
    f = open('logs/latest.log')
    content = f.read()
    if previousContent != '':
        if previousContent in content:
            content.replace(previousContent,'')
            if content != '':
                print(content)
    previousContent = f.read()
    f.close()

    #stdin
    command = input('')
    if command:
        if command == 'stop':
            server.stdin.write(bytes('stop\r\n', 'ascii'))
            server.stdin.flush()
            time.sleep(20)
            sys.exit()
        else:
            server.stdin.write(bytes(command + '\r\n', 'ascii'))
            server.stdin.flush()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM