简体   繁体   中英

Python, Stop a Thread

I'm trying to create a class that pings an ip address and keeps a record for connected/ not connected times.

Since this class is a part of a GUI, I wish to stop this thread when asked by user.

Found some Q&As regrading this issue, but neither one actually causes thread to stop.

I'm trying to make a method, a part of this class that will stop self.run()

Here's my Pinger class:

class Pinger(threading.Thread):
    def __init__(self, address='', rate=1):
        threading.Thread.__init__(self)

        self.address = address
        self.ping_rate = rate
        self.ping_vector, self.last_ping = [], -1
        self.start_time, self.last_status = datetime.datetime.now(), []
        self.timestamp, self.time_vector = 0, [datetime.timedelta(0)] * 4

    def run(self):
            self.start_ping()

    def start_ping(self):
        self.timestamp = datetime.datetime.now()
        while True:
            ping_result = os.system('ping %s -n 1 >Null' % self.address)
            self.ping_vector.append(ping_result)

            if self.last_ping != ping_result:
                text = ['Reachable', 'Lost']
                print(str(self.timestamp)[:-4], self.address, text[ping_result])

            round_time_qouta = datetime.datetime.now() - self.timestamp
            self.timestamp = datetime.datetime.now()
            self.update_time_counter(ping_result, round_time_qouta)

            self.last_ping = ping_result
            time.sleep(self.ping_rate)

    def update_time_counter(self, ping_result=0, time_quota=datetime.timedelta(0)):
        """self.time_vector = [[cons.succ ping time],[cons.not_succ ping time],
        [max accum succ ping time],[max accum not_succ ping time] """

        p_vec = [0, 1]

        self.time_vector[p_vec[ping_result]] += time_quota
        if self.time_vector[p_vec[ping_result]].total_seconds() > self.time_vector[
            p_vec[ping_result] + 2].total_seconds():
            self.time_vector[p_vec[ping_result] + 2] = self.time_vector[p_vec[ping_result]]

        self.time_vector[p_vec[ping_result - 1]] = datetime.timedelta(0)

        self.last_status = [ping_result, self.chop_milisecond(self.time_vector[ping_result]),
                            self.chop_milisecond(self.time_vector[ping_result + 2]),
                            self.chop_milisecond(datetime.datetime.now() - self.start_time)]

        print(str(self.timestamp)[:-4], "State: " + ['Received', 'Lost'][ping_result],
              " Duration: " + self.last_status[1], " Max Duration: " + self.last_status[2],
              "Total time: " + self.last_status[3])

    def chop_milisecond(self, time):
        return str(time).split('.')[0]

As I was saying in my comment, the easiest way is to use threading.Event to signal your thread when it should exit. That way you can expose the event and let other threads set it while you can check for its state from within your thread and exit on request.

In your case, it could be as simple as:

class Pinger(threading.Thread):

    def __init__(self, address='', rate=1):
        threading.Thread.__init__(self)
        self.kill = threading.Event()
        # the rest of your setup...

    # etc.

    def start_ping(self):
        self.timestamp = datetime.datetime.now()
        while not self.kill.is_set():
            # do your pinging stuff

    # etc.

Then whenever you want the thread stopped (like from your UI), just call on it: pinger_instance.kill.set() and you're done. Keep in mind, tho, that it will take some time for it to get killed due to the blocking os.system() call and due to the time.sleep() you have at the end of your Pinger.start_ping() method.

use _Thread_stop():

MyPinger._Thread__stop()

I would code your class a little bit differently, to run as a daemon.

leave start_ping code out and use next code :

MyPinger = threading.Thread(target = self.start_ping, name="Pinger")
MyPinger.setDaemon(True)
MyPinger.start() # launch start_ping

and may use _Thread_stop() to stop it , that's a little bit brute...:

if MyPinger.IsAlive():
   MyPinger._Thread__stop() 

thanks for @zwer for the lead. Here's my complete code ( changes are marked )

class Pinger(threading.Thread):
    def __init__(self, address='', rate=1):
        threading.Thread.__init__(self)

        self.address = address
        self.ping_rate = rate
        self.ping_vector, self.last_ping = [], -1
        self.start_time, self.last_status = datetime.datetime.now(), []
        self.timestamp, self.time_vector = 0, [datetime.timedelta(0)] * 4
        self.event = threading.Event() # <---- Added

    def run(self):
        while not self.event.is_set(): # <---- Added
            self.start_ping()
            self.event.wait(self.ping_rate) # <---- Added ( Time to repeat moved in here )

    def stop(self):       # <---- Added ( ease of use )
        self.event.set()  # <---- Added ( set to False and causes to stop )


    def start_ping(self):
        self.timestamp = datetime.datetime.now()
        # While loop ##--- > Deleted. now it loops in run method #####
        ping_result = os.system('ping %s -n 1 >Null' % self.address)
        self.ping_vector.append(ping_result)

        if self.last_ping != ping_result:
            text = ['Reachable', 'Lost']
            print(str(self.timestamp)[:-4], self.address, text[ping_result])

        round_time_qouta = datetime.datetime.now() - self.timestamp
        self.timestamp = datetime.datetime.now()
        self.update_time_counter(ping_result, round_time_qouta)

        self.last_ping = ping_result
        #### time.sleep (self.ping_rate)  # <---- deleted 

    def update_time_counter(self, ping_result=0, time_quota=datetime.timedelta(0)):
        """self.time_vector = [[cons.succ ping time],[cons.not_succ ping time],
        [max accum succ ping time],[max accum not_succ ping time] """

        p_vec = [0, 1]

        self.time_vector[p_vec[ping_result]] += time_quota
        if self.time_vector[p_vec[ping_result]].total_seconds() > self.time_vector[
            p_vec[ping_result] + 2].total_seconds():
            self.time_vector[p_vec[ping_result] + 2] = self.time_vector[p_vec[ping_result]]

        self.time_vector[p_vec[ping_result - 1]] = datetime.timedelta(0)

        self.last_status = [ping_result, self.chop_milisecond(self.time_vector[ping_result]),
                            self.chop_milisecond(self.time_vector[ping_result + 2]),
                            self.chop_milisecond(datetime.datetime.now() - self.start_time)]

        print(str(self.timestamp)[:-4], "State: " + ['Received', 'Lost'][ping_result],
              " Duration: " + self.last_status[1], " Max Duration: " + self.last_status[2],
              "Total time: " + self.last_status[3])

    def chop_milisecond(self, time):
        return str(time).split('.')[0]

    def get_status(self):
        return self.last_status


c = Pinger('127.0.0.1', 5)
c.start()
time.sleep(10)
c.stop()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM