最近业余爱好是吃了好几个月灰的树莓派,开始补全各种driver,也算是对Python的学习。写driver的过程中,第一次遇到多线程的情况。虽然没太费力就实现了想要的效果,不过还是全面学习一下比较合适。
1. 在线程中申请异常处理 和 2. 设置/重置停止符是比较常用的方法,我也用的差不多。
3.使用跟踪杀死线程)肯定是炫技的人才用的。
4. 使用多进程模块杀死线程,5.用设置为后台的方式杀死线程仅仅是为了提醒大家线程的工作原理,算是不错的知识吧。
6. 使用隐藏函数_stop()我觉得就是搞笑的。
以上是观点,以下是事实:
下文来自GeeksforGeeks:Python | Different ways to kill a Thread - GeeksforGeekswww.geeksforgeeks.org
Python | Different ways to kill a Thread
通常来说,突然杀死线程是不好的编程习惯。突然杀死一个线程,可能会让一个需要恰当关闭的关键资源无法释放。但是你可能希望杀死一个线程,在过去一段时间或者某些中断发生后。以下是杀死线程的不同方法:Raising exceptions in a python thread 在线程中申请异常处理
Set/Reset stop flag 设置/重置停止符
Using traces to kill threads 使用跟踪杀死线程
Using the multiprocessing module to kill threads 使用多进程模块杀死线程
Killing Python thread by setting it as daemon 用设置为后台(daemon)的方式杀死线程
Using a hidden function _stop() 使用隐藏函数_stop()Raising exceptions in a python thread : 在线程中申请异常处理
# Python program raising exceptions in a python thread
import threading
import ctypes
import time
class thread_with_exception(threading.Thread):
def __init__(self, name):
threading.Thread.__init__(self)
self.name = name
def run(self):
# target function of the thread class
try: # 用try/finally 的方式处理exception,从而kill thread
while True:
print('running ' + self.name)
finally:
print('ended')
def get_id(self):
# returns id of the respective thread
if hasattr(self, '_thread_id'):
return self._thread_id
for id, thread in threading._active.items():
if thread is self:
return id
def raise_exception(self):
thread_id = self.get_id()
#精髓就是这句话,给线程发过去一个exceptions,线程就那边响应完就停了
res = ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id,
ctypes.py_object(SystemExit))
if res > 1:
ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id, 0)
print('Exception raise failure')
t1 = thread_with_exception('Thread 1')
t1.start()
time.sleep(2)
t1.raise_exception()
t1.join()
当我们跑上面的代码,你会注意到,只要函数raise_exception() 被调用,目标函数 run() 就结束了。这是因为只要申请exception,程序控制会跳到try代码段,然后run()函数就被终止了。在那之后,join()函数能被调用来杀死线程。如果没有函数raise_exception(),目标函数run() 就会永远跑,join()函数也没法被调用来杀死线程。
2. Set/Reset stop flag :设置/重置停止符
为了杀死一个线程,我们能声明一个停止符,并在线程中时不时的看一下这个停止符。例如:
# Python program showing how to kill threads using set/reset stop flag
import threading
import time
def run():
while True:
print('thread running')
global stop_threads
if stop_threads: #精髓在这里,这算是最直接的方法了,我也用的这个方法
break
stop_threads = False
t1 = threading.Thread(target = run)
t1.start()
time.sleep(1)
stop_threads = True
t1.join()
print('thread killed')
上面的代码中,只要全局变量stop_threads被设置,目标函数run()就停止了,然后我们就能用join()来杀掉线程t1。但是有时候我们希望能避免使用全局变量。为这种情况,我们可以为函数传入一个对象从而提供相似的功能。代码如下:
# Python program killing threads using stop flag
import threading
import time
def run(stop):
while True:
print('thread running')
if stop():
break
def main():
stop_threads = False
t1 = threading.Thread(target = run, args =(lambda : stop_threads, ))
t1.start()
time.sleep(1)
stop_threads = True
t1.join()
print('thread killed')
main()
我们为目标函数run()传入了一个终止对象stop,只要在main函数本地改变stop的值,就可以杀死线程了。
3. Using traces to kill threads : 使用跟踪杀死线程
这个方法中,我们为每一个线程都加入跟踪(traces)。每一个trace发现什么刺激或者标记的时候就会停止自身,也就杀死了对应的线程。例如:
# Python program using traces to kill threads
import sys
import trace
import threading
import time
class thread_with_trace(threading.Thread):
def __init__(self, *args, **keywords):
threading.Thread.__init__(self, *args, **keywords)
self.killed = False
def start(self):
self.__run_backup = self.run
self.run = self.__run
threading.Thread.start(self)
def __run(self):
sys.settrace(self.globaltrace) #精髓在这里,但我还没有理解为什么要这么做。。。
self.__run_backup()
self.run = self.__run_backup
def globaltrace(self, frame, event, arg):
if event == 'call':
return self.localtrace
else:
return None
def localtrace(self, frame, event, arg):
if self.killed:
if event == 'line':
raise SystemExit()
return self.localtrace
def kill(self):
self.killed = True
def func():
while True:
print('thread running')
t1 = thread_with_trace(target = func)
t1.start()
time.sleep(2)
t1.kill()
t1.join()
if not t1.isAlive():
print('thread killed')
在上面的代码中,start()被改变而加入了系统跟踪函数
4. Using the multiprocessing module to kill threads : 使用多进程模块杀死线程
Python的多进程模块允许你开启多个进程如同你开启多个线程一样。多进程模块的接口和多线程模块相似。例如,以下代码创建了3个线程从1数到9:
# Python program creating three threads
import threading
import time
# counts from 1 to 9
def func(number):
for i in range(1, 10):
time.sleep(0.01)
print('Thread ' + str(number) + ': prints ' + str(number*i))
# creates 3 threads
for i in range(0, 3):
thread = threading.Thread(target=func, args=(i,))
thread.start()
以下代码稍作改变,用进程代替了线程:
# Python program creating thread using multiprocessing module
import multiprocessing
import time
def func(number):
for i in range(1, 10):
time.sleep(0.01)
print('Processing ' + str(number) + ': prints ' + str(number*i))
for i in range(0, 3):
process = multiprocessing.Process(target=func, args=(i,))
process.start()
尽管两种模块的接口相似,但其实实现的方式非常不同。所有的线程都能分享全局变量,而进程之间完全独立。因此,杀死一个进程比之线程要安全的多。Process类提供了方法terminate()()来杀死进程。现在,让我们回到一开始的问题。对于上面的代码,如果我们想在0.03秒后杀死所有进程,我们只需要如下代码:
# Python program killing a thread using multiprocessing module
import multiprocessing
import time
def func(number):
for i in range(1, 10):
time.sleep(0.01)
print('Processing ' + str(number) + ': prints ' + str(number*i))
# list of all processes, so that they can be killed afterwards
all_processes = []
for i in range(0, 3):
process = multiprocessing.Process(target=func, args=(i,))
process.start()
all_processes.append(process)
# kill all processes after 0.03s
time.sleep(0.03)
for process in all_processes:
process.terminate() #精髓在这里
尽管这两种模块非常不同。多进程提供的功能有时和多线程非常相似,因此记得多进程有时可以作为多线程的代替,并且多进程是自带kill的。
5. Killing Python thread by setting it as daemon : 用设置为后台的方式杀死线程
后台线程是当主程序退出时杀死的线程。例如:
import threading
import time
import sys
def func():
while True:
time.sleep(0.5)
print('Thread alive, and it won't die on program termination')
t1 = threading.Thread(target=func)
t1.start()
time.sleep(2)
sys.exit()
运行程序后发现,线程t1保存活动并阻止了主程序用sys.exit()的方式退出。在Python中,任何活跃的非后台线程都会阻止主程序退出。然而,后台线程在主程序退出时就能自动杀死。为了把一个线程设置为后台,我们设置关键词daemon as True。例子如下:
# Python program killing thread using daemon
import threading
import time
import sys
def func():
while True:
time.sleep(0.5)
print('Thread alive, but it will die on program termination')
t1 = threading.Thread(target=func)
t1.daemon = True #精髓在这里
t1.start()
time.sleep(2)
sys.exit()
运行后可知,只要主程序退出,线程t1就能被杀死。在程序终止时就杀掉所有线程的情况下,这个方法非常有用。值得注意的是,在Python中,当所有非后台线程杀死后,主程序就会终止,而不管是否有后台线程任然活跃。因此,后台线程使用的资源,比如打开文件,数据库操作等,可能不能恰当的释放。这是为什么线程在Python中默认不是后台线程。强行杀死一个线程是不推荐的,除非你清楚的知道后果不会导致内存泄漏或者死锁。
6. Using a hidden function_stop():使用隐藏函数_stop()
为了杀死线程,我们可以使用隐藏函数_stop()。这个函数没有被记录在官方资料,所以有可能在下个版本就不能用了。
# Python program killing a thread using ._stop() function
import time
import threading
class MyThread(threading.Thread):
# Thread class with a _stop() method.
# The thread itself has to check
# regularly for the stopped() condition.
def __init__(self, *args, **kwargs):
super(MyThread, self).__init__(*args, **kwargs)
self._stop = threading.Event()
# function using _stop function
def stop(self):
self._stop.set()
def stopped(self):
return self._stop.isSet()
def run(self):
while True:
if self.stopped():
return
print("Hello, world!")
time.sleep(1)
t1 = MyThread()
t1.start()
time.sleep(5)
t1.stop()
t1.join()