posts - 217, comments - 61, trackbacks - 0, articles - 0
   :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理
先上正确的代码
参考 https://blog.csdn.net/vinsuan1993/article/details/78158589/
import threading
import time
import inspect
import ctypes

class KillableThread(threading.Thread):
    
def __init__(self, *args, **kw):
        super(KillableThread, self).
__init__(*args, **kw)

    
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 kill(self):
        KillableThread._async_raise(self.ident, SystemExit)


# 循环输出s,t次,每0.6秒一次
def say(s,t):
    
for i in range(t):
        
print(s)
        time.sleep(
0.6)

if __name__ == "__main__":
    thread1 
= KillableThread(target=say, args=("Say 3 times"3))
    thread1.start()    
    thread1.join()
    
print("Alive? ", thread1.is_alive())
    thread2 
= KillableThread(target=say, args=("Say 999 times"999))
    thread2.start()
    thread2.join(
2)
    
print("Alive? ", thread2.is_alive())
    thread2.kill()
    
print("Still alive? ", thread2.is_alive())
    
# 查看线程数量
    while True:
        thread_num 
= len(threading.enumerate())
        
print("线程数量是%d" % thread_num)
        
if thread_num <= 1:
            
break
        time.sleep(
1)
    
print("Still alive? ", thread2.is_alive())
输出为
Say 3 times
Say 3 times
Say 3 times
Alive?  False
Say 999 times
Say 999 times
Say 999 times
Say 999 times
Alive?  True
Still alive?  True
线程数量是2
线程数量是1
Still alive?  False

------ 分割线以下分析是错的 ------

后面的代码是错误的,错误在于setDaemon是让被设置的线程随主线程一起结束,而不是父线程,我不清楚这样有什么意义。之所以没有发现下面代码是错的,是因为正好主线程后面就结束了,如果让主线程继续空转就会看到999那个线程还在输出
Python中的threading.Thread并没有提供kill方法杀死尚未结束的线程,但是提供了setDaemon方法设置守护线程,守护线程会在父线程结束时自动结束。
注意父线程是调用线程start()的线程,而不是创建threading.Thread对象的线程,下面的代码演示了通过setDaemon方法让线程超时自动结束。


import threading
import time

def start_thread_with_timeout(target, secs:int, args=None):
    th 
= threading.Thread(target=target, args=args)
    timeout 
= True
    
def daemon_thread():
        
'''
        对于嵌套定义的函数,当Python发现读取一个不存在的变量时,
        会向外层去找,而当给一个变量赋值时,若函数内不存在此变量,
        优先创建而不向外找,所以此处timeout必须声明为nonlocal,
        而对th的声明是可选的
        
'''
        
# 声明外部变量
        nonlocal timeout, th
        th.setDaemon(True) 
# 设置为守护线程,会在它的父线程结束时自动结束
        th.start()
        th.join(secs)
        timeout 
= th.is_alive()
    guard 
= threading.Thread(target=daemon_thread)
    guard.start()
    guard.join()
    
return timeout

# 循环输出s,t次,每0.6秒一次
def say(s,t):
    
for i in range(t):
        
print(s)
        time.sleep(
0.6)

if __name__ == "__main__":
    
# 输出字符串3次,每次间隔0.6s,2s后超时,所以此处应该不会超时结束
    timeout = start_thread_with_timeout(target=say, args=("Say 3 times"3), secs=2)
    
print("Timeout? ", timeout)
    
# 输出字符串999次,每次间隔0.6s,2s后超时,所以此处应该不会超时结束
    timeout = start_thread_with_timeout(target=say, args=("Say 999 times"999), secs=2)
    
print("Timeout? ", timeout)
程序输出为:
Say 3 times
Say 3 times
Say 3 times
Timeout?  False
Say 999 times
Say 999 times
Say 999 times
Say 999 times
Timeout?  True
清楚原理后也可以用来实现kill的功能,例如可以让哨兵线程每隔一个时间片查询一次需要kill的flag,不过这样没有办法实时杀死线程,时间片设太大就延迟杀死,设太小会频繁占用CPU,解决方法可以通过信号量(semaphore)或者条件变量(condition)让哨兵线程陷入睡眠,外部调用kill唤醒哨兵线程直接结束就可以了。
(哨兵线程:守卫线程的父线程,可以看做是真正要执行的线程的哨兵,所以我称之为哨兵线程)

import threading
import time

class KillableThread:
    
def __init__(self, target, args=None):
        self.th 
= threading.Thread(target=target, args=args)
        self.kill_sema 
= threading.Semaphore(0)
        self.start_sema 
= threading.Semaphore(0)
        
def daemon_thread(self):
            self.th.setDaemon(True)
            self.th.start()
            self.start_sema.release()
            self.kill_sema.acquire()
        self.guard 
= threading.Thread(target=daemon_thread, args=(self,))

    
def start(self):
        self.guard.start()
        self.start_sema.acquire()

    
def join(self, secs=None):
        self.th.join(secs)
        
if not self.th.is_alive():
            self.kill_sema.release()

    
def is_alive(self):
        
return self.th.is_alive() and self.guard.is_alive()

    
def kill(self):
        self.kill_sema.release()
        
while self.guard.is_alive():
            
pass
    


# 循环输出s,t次,每0.6秒一次
def say(s,t):
    
for i in range(t):
        
print(s)
        time.sleep(
0.6)

if __name__ == "__main__":
    thread1 
= KillableThread(target=say, args=("Say 3 times"3))
    thread1.start()
    thread1.join()
    
print("Alive? ", thread1.is_alive())
    thread2 
= KillableThread(target=say, args=("Say 999 times"999))
    thread2.start()
    thread2.join(
2)
    
print("Alive? ", thread2.is_alive())
    thread2.kill()
    
print("Still alive? ", thread2.is_alive())
只有注册用户登录后才能发表评论。