强制结束线程,异步线程

场景: 如果某个线程持续阻塞,无法退出,从而导致整个程序无法结束,此时就需要强制结束线程

 

思路:由于程序阻塞,比如卡在代码中的某一行后,一直无法向下执行,此时,无法通过常规方式结束线程

 

方法1: 采用线程自己的方法强制结束  Thread._Thread__stop(thd_obj),此种方式的优点是可以强制结束在某一行代码处卡住或者正在运行的线程

注:此处的卡住指的是 线程正在发生io阻塞,此时,无法判断事件,只能在底层加超时处理机制或者采用此处的方式暴力结束,别无他法。

方法2: 使用ctypes的pythonapi接口结束线程,此种方式只能强制结束正在运行的线程而不报错,无法结束被卡主不动的线程,此种方式无法结束python3的线程

 

划重点:python私有变量表示在内的内部定义并使用,外部无法訪问,以双下划线作为前作,定义后被python转为

_classname__变量名

 

代码示例1:

import sys
import time
import inspect
import ctypes
import platform

from datetime import datetime
from threading import Thread


def _async_raise(tid, exctype):
    """raises the exception, performs cleanup if needed"""
    tid = ctypes.c_long(tid)
    if not inspect.isclass(exctype):
        exctype = type(exctype)
    res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype))
    if res == 0:
        raise ValueError("invalid thread id")
    elif res != 1:
        # """if it returns a number greater than one, you're in trouble,
        # and you should call it again with exc=NULL to revert the effect"""
        ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, None)
        raise SystemError("PyThreadState_SetAsyncExc failed")


def stop_thread(thd_obj):
    if platform.python_version() < '3':
        try:
            Thread._Thread.__stop(thd_obj)
       # Thread._Thread__stop(thd_obj) # 此种方法也可以
except: pass else: _async_raise(thd_obj.ident, SystemExit) class commThread(Thread): def __init__(self, target=None, name=None, args=()): super(commThread, self).__init__() self.setName(str(name)) self.fn = target self.args = args self.result = '' def run(self): print("begin run the child thread: %s" % ) while True: time.sleep(1) try: if self.fn: self.result = self.fn(*self.args) finally: pass def get_result(self): return self.result def get_now_time(): return time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())) def time_str_to_time(st_date, end_date, mod='%Y-%m-%d %H:%M:%S', to_hour=False, to_minute=False, to_second=True): st_date = datetime.strptime(st_date, mod) end_date = datetime.strptime(end_date, mod) time_interval = end_date - st_date if to_second: return time_interval.total_seconds() if to_minute: return if to_hour: return class A(): def __init__(self): pass def test_ret(self): return 'hello world' def task1_fn(self, url): while True: time.sleep(1000000) print (url + " FINISHED " + self.test_ret()) def timeout_check(self, st_time): while True: diff = time_str_to_time(st_time, get_now_time()) print('check time out is running, diff: %s.' % diff) if diff >= 5: break time.sleep(1) return True if __name__ == '__main__': a = A() t1 = commThread(target=a.task1_fn, args=('task1_fn',), name='task1_fn') st_time = get_now_time() t2 = commThread(target=a.timeout_check, args=(st_time,), name='task2_fn') t1.start() t2.start() while True: t2_result = t2.get_result() print('t2 result: %s' % t2_result) if t2_result: try: stop_thread(t1) stop_thread(t2) # Thread._Thread__stop(t1) # Thread._Thread__stop(t2) # except SystemError as sys_err: # print(sys_err) # except ValueError as val_err: # print(val_err) # except Exception as e: # print(e) except: pass break time.sleep(1) print('over .......') sys.exit()

 

代码示例2:

from threading import Thread
import time


class commThread(Thread):
    def __init__(self, target=None, name=None, args=()):
        super(commThread, self).__init__()
        self.setName(str(name))
        self.fn = target
        self.args = args
        self.result = ''

    def run(self):
        print("begin run the child thread: %s" % )
        while True:
            time.sleep(1)
            try:
                if self.fn:
                    self.result = self.fn(*self.args)
            finally:
                pass

    def get_result(self):
        return self.result

    def kill(self):
        try:
            self._Thread__stop()
        except:
            pass


def do_scan():
    while True:
        print('scan is running.....')
        time.sleep(5)


if __name__ == '__main__':
    for i in range(1):
        t1 = commThread(target=do_scan, name='do_scan')
        t1.setDaemon(True)
        t1.start()
        t1.join(timeout=3)

        print(t1.get_result())
        if t1.is_alive():
            t1.kill()

    print('over................')

 

示例2中充分运用了 setDaemon和join的作用:

1)setDaemon为True表示守护线程或者后台线程,主线程或者进程退出时,无需等待此线程完成,意义在于:避免子线程无限死循环导致无法退出程序,避免了孤儿进程的出现,设置为False刚好相反,会等待子线程执行完成;

2)join的作用是优先占用cpu资源。join中的timeout表示优先占用cpu资源的最大时长,如果没有设置,则一直等待直到子线程结束,如果设置了,则达到超时时间后,子线程自动退出。

 

关于强制结束线程的理解:

  python中未提供强制结束线程的接口,原因是这样做并不安全,

  理想的停止退出线程方法是 让线程自个自杀,所谓的线程自杀就是 我们给线程一个标志位,线程检测并满足标志位条件后自己退出。

  python的线程不是模拟的,是真实的内核线程,内核调用pthread方法,但Python上层没有提供关闭线程的方法,这就需要我们自己把握了。

 

python结束线程的两种方法:

1) 退出标记
2) 使用ctypes强行杀掉线程

 

python3强制结束IO阻塞的线程暂未找到合适的方法,如果哪位马油有方法,还原留言区讨论。