完工前我就以为有什么不太对劲,觉得要背锅。这可不,上班第三天就捅锅了。
我们有个了不得的后台顺序,能够动态加载模块,并以线程体式格局运转,经由过程这类情势完成插件的功用。而模块更新时刻,后台顺序自身不会退出,只会将模块对应的线程封闭、更新代码再启动,6 得不可。
因而乎我就写了个模块预备大展技艺,效果遗忘写退出函数了,致使每次更新模块都新建立一个线程,除非重启谁人顺序,不然那些线程就一向苟在世。
这可不可啊,得想个方法清算呀,要不然怕是要炸了。
那末怎样清算呢?我能想到的就是两步走:
找出须要清算的线程号 tid;
烧毁它们;
找出线程ID
和日常平凡的毛病排查类似,先经由过程 ps 敕令看看目的历程的线程状况,由于已是 setName 设置过线程名,所以一般来讲应当是看到对应的线程的。 直接用下面代码来模仿这个线程:
Python 版本的多线程
#coding: utf8 import threading import os import time def tt(): info = threading.currentThread() while True: print 'pid: ', os.getpid() print info.name, info.ident time.sleep(3) t1 = threading.Thread(target=tt) t1.setName('OOOOOPPPPP') t1.setDaemon(True) t1.start() t2 = threading.Thread(target=tt) t2.setName('EEEEEEEEE') t2.setDaemon(True) t2.start() t1.join() t2.join()
输出:
root@10-46-33-56:~# python t.py pid: 5613 OOOOOPPPPP 139693508122368 pid: 5613 EEEEEEEEE 139693497632512 ...
能够看到在 Python 内里输出的线程名就是我们设置的那样,然则 Ps 的效果倒是令我疑心人生:
root@10-46-33-56:~# ps -Tp 5613 PID SPID TTY TIME CMD 5613 5613 pts/2 00:00:00 python 5613 5614 pts/2 00:00:00 python 5613 5615 pts/2 00:00:00 python
一般来讲不该是如许呀,我有点迷了,岂非我一向都是记错了?用别的言语版本的多线程来测试下:
C 版本的多线程
#include<stdio.h> #include<sys/syscall.h> #include<sys/prctl.h> #include<pthread.h> void *test(void *name) { pid_t pid, tid; pid = getpid(); tid = syscall(__NR_gettid); char *tname = (char *)name; // 设置线程名字 prctl(PR_SET_NAME, tname); while(1) { printf("pid: %d, thread_id: %u, t_name: %s\n", pid, tid, tname); sleep(3); } } int main() { pthread_t t1, t2; void *ret; pthread_create(&t1, NULL, test, (void *)"Love_test_1"); pthread_create(&t2, NULL, test, (void *)"Love_test_2"); pthread_join(t1, &ret); pthread_join(t2, &ret); }
输出:
root@10-46-33-56:~# gcc t.c -lpthread && ./a.out pid: 5575, thread_id: 5577, t_name: Love_test_2 pid: 5575, thread_id: 5576, t_name: Love_test_1 pid: 5575, thread_id: 5577, t_name: Love_test_2 pid: 5575, thread_id: 5576, t_name: Love_test_1 ...
用 PS 敕令再次考证:
root@10-46-33-56:~# ps -Tp 5575 PID SPID TTY TIME CMD 5575 5575 pts/2 00:00:00 a.out 5575 5576 pts/2 00:00:00 Love_test_1 5575 5577 pts/2 00:00:00 Love_test_2
这个才是准确嘛,线程名确实是能够经由过程 Ps 看出来的嘛!
不过为啥 Python 谁人看不到呢?既然是经由过程 setName
设置线程名的,那就看看定义咯:
[threading.py] class Thread(_Verbose): ... @property def name(self): """A string used for identification purposes only. It has no semantics. Multiple threads may be given the same name. The initial name is set by the constructor. """ assert self.__initialized, "Thread.__init__() not called" return self.__name @name.setter def name(self, name): assert self.__initialized, "Thread.__init__() not called" self.__name = str(name) def setName(self, name): self.name = name ...
看到这里实在只是在 Thread
对象的属性设置了罢了,并没有动到基础,那一定就是看不到咯~
如许看起来,我们已没方法经由过程 ps
或许 /proc/
这类手腕在外部搜刮 python 线程名了,所以我们只能在 Python 内部来处理。
因而题目就变成了,怎样在 Python 内部拿到一切正在运转的线程呢?
threading.enumerate
能够圆满处理这个题目!Why?
Because 在下面这个函数的 doc 内里说得很清晰了,返回一切活泼的线程对象,不包括住手和未启动的。
[threading.py] def enumerate(): """Return a list of all Thread objects currently alive. The list includes daemonic threads, dummy thread objects created by current_thread(), and the main thread. It excludes terminated threads and threads that have not yet been started. """ with _active_limbo_lock: return _active.values() + _limbo.values()
由于拿到的是 Thread 的对象,所以我们经由过程这个能到该线程相干的信息!
请看完全代码示例:
#coding: utf8 import threading import os import time def get_thread(): pid = os.getpid() while True: ts = threading.enumerate() print '------- Running threads On Pid: %d -------' % pid for t in ts: print t.name, t.ident print time.sleep(1) def tt(): info = threading.currentThread() pid = os.getpid() while True: print 'pid: {}, tid: {}, tname: {}'.format(pid, info.name, info.ident) time.sleep(3) return t1 = threading.Thread(target=tt) t1.setName('Thread-test1') t1.setDaemon(True) t1.start() t2 = threading.Thread(target=tt) t2.setName('Thread-test2') t2.setDaemon(True) t2.start() t3 = threading.Thread(target=get_thread) t3.setName('Checker') t3.setDaemon(True) t3.start() t1.join() t2.join() t3.join()
输出:
root@10-46-33-56:~# python t_show.py pid: 6258, tid: Thread-test1, tname: 139907597162240 pid: 6258, tid: Thread-test2, tname: 139907586672384 ------- Running threads On Pid: 6258 ------- MainThread 139907616806656 Thread-test1 139907597162240 Checker 139907576182528 Thread-test2 139907586672384 ------- Running threads On Pid: 6258 ------- MainThread 139907616806656 Thread-test1 139907597162240 Checker 139907576182528 Thread-test2 139907586672384 ------- Running threads On Pid: 6258 ------- MainThread 139907616806656 Thread-test1 139907597162240 Checker 139907576182528 Thread-test2 139907586672384 ------- Running threads On Pid: 6258 ------- MainThread 139907616806656 Checker 139907576182528 ...
代码看起来有点长,然则逻辑相称简朴,Thread-test1
和 Thread-test2
都是打印出当前的 pid、线程 id 和 线程名字,然后 3s 后退出,这个是想模仿线程一般退出。
而 Checker
线程则是每秒经由过程 threading.enumerate
输出当前历程内一切活泼的线程。
能够显著看到一最先是能够看到 Thread-test1
和 Thread-test2
的信息,当它俩退出以后就只剩下 MainThread
和 Checker
自身罢了了。
烧毁指定线程
既然能拿到名字和线程 id,那我们也就能干掉指定的线程了!
假定如今 Thread-test2
已黑化,发疯了,我们须要阻止它,那我们就能够经由过程这类体式格局处理了:
在上面的代码基础上,增添和补上以下代码:
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: ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, None) raise SystemError("PyThreadState_SetAsyncExc failed") def stop_thread(thread): _async_raise(thread.ident, SystemExit) def get_thread(): pid = os.getpid() while True: ts = threading.enumerate() print '------- Running threads On Pid: %d -------' % pid for t in ts: print t.name, t.ident, t.is_alive() if t.name == 'Thread-test2': print 'I am go dying! Please take care of yourself and drink more hot water!' stop_thread(t) print time.sleep(1)
输出
root@10-46-33-56:~# python t_show.py pid: 6362, tid: 139901682108160, tname: Thread-test1 pid: 6362, tid: 139901671618304, tname: Thread-test2 ------- Running threads On Pid: 6362 ------- MainThread 139901706389248 True Thread-test1 139901682108160 True Checker 139901661128448 True Thread-test2 139901671618304 True Thread-test2: I am go dying. Please take care of yourself and drink more hot water! ------- Running threads On Pid: 6362 ------- MainThread 139901706389248 True Thread-test1 139901682108160 True Checker 139901661128448 True Thread-test2 139901671618304 True Thread-test2: I am go dying. Please take care of yourself and drink more hot water! pid: 6362, tid: 139901682108160, tname: Thread-test1 ------- Running threads On Pid: 6362 ------- MainThread 139901706389248 True Thread-test1 139901682108160 True Checker 139901661128448 True // Thread-test2 已不在了
一顿操纵下来,虽然我们如许看待 Thread-test2
,但它照样体贴着我们:多喝热水,
PS: 热水虽好,八杯足矣,请勿贪酒哦。
书回正传,上述的要领是极为粗犷的,为何这么说呢?
由于它的道理是:应用 Python 内置的 API,触发指定线程的非常,让其能够自动退出;
万不得已真不要用这类要领,有一定概率触发不可形貌的题目。牢记!别问我为何会晓得...
为何住手线程这么难
多线程自身设想就是在历程下的合作并发,是调理的最小单位,线程间分食着历程的资本,所以会有许多锁机制和状况掌握。
假如运用强制手腕干掉线程,那末很大概率涌现意想不到的bug。 而且最主要的锁资本开释能够也会涌现意想不到题目。
我们以至也没法经由过程信号杀死历程那样直接杀线程,由于 kill 只要应付历程才到达我们的预期,而应付线程显著不能够,不论杀哪一个线程,全部历程都邑退出!
而由于有 GIL,使得许多童鞋都以为 Python 的线程是Python 自行完成出来的,并不是现实存在,Python 应当能够直接烧毁吧?
然则事实上 Python 的线程都是名副实在的线程!
什么意思呢?Python 的线程是操纵体系经由过程 pthread 建立的原生线程。Python 只是经由过程 GIL 来束缚这些线程,来决议什么时刻最先调理,比方说运转了多少个指令就交出 GIL,至于谁夺得花魁,得听操纵体系的。
假如是纯真的线程,实在体系是有方法住手的,比方: pthread_exit
,pthread_kill
或 pthread_cancel
, 概况可看:https://www.cnblogs.com/Creat...
很可惜的是: Python 层面并没有这些要领的封装!我的天,好气!能够人家以为,线程就该温顺看待吧。
怎样温顺退出线程
想要温顺退出线程,实在差不多就是一句空话了~
要么运转完退出,要么设置标志位,经常搜检标记位,该退出的就退出咯。
扩大
《怎样准确的住手正在运转的子线程》:https://www.cnblogs.com/Creat...
《不要粗犷的烧毁python线程》:http://xiaorui.cc/2017/02/22/...
迎接列位大神指导交换, QQ议论群: 258498217
转载请说明泉源: https://segmentfault.com/a/11...
专栏
手艺栈大杂烩
文章概况
3 天前宣布
Python:线程之定位与烧毁
266 次浏览 · 读完须要 30 分钟
8
背景
完工前我就以为有什么不太对劲,觉得要背锅。这可不,上班第三天就捅锅了。
我们有个了不得的后台顺序,能够动态加载模块,并以线程体式格局运转,经由过程这类情势完成插件的功用。而模块更新时刻,后台顺序自身不会退出,只会将模块对应的线程封闭、更新代码再启动,6 得不可。
因而乎我就写了个模块预备大展技艺,效果遗忘写退出函数了,致使每次更新模块都新建立一个线程,除非重启谁人顺序,不然那些线程就一向苟在世。
这可不可啊,得想个方法清算呀,要不然怕是要炸了。
那末怎样清算呢?我能想到的就是两步走:
找出须要清算的线程号 tid;
烧毁它们;
找出线程ID
和日常平凡的毛病排查类似,先经由过程 ps 敕令看看目的历程的线程状况,由于已是 setName 设置过线程名,所以一般来讲应当是看到对应的线程的。 直接用下面代码来模仿这个线程:
Python 版本的多线程
#coding: utf8 import threading import os import time def tt(): info = threading.currentThread() while True: print 'pid: ', os.getpid() print info.name, info.ident time.sleep(3) t1 = threading.Thread(target=tt) t1.setName('OOOOOPPPPP') t1.setDaemon(True) t1.start() t2 = threading.Thread(target=tt) t2.setName('EEEEEEEEE') t2.setDaemon(True) t2.start() t1.join() t2.join()
输出:
root@10-46-33-56:~# python t.py pid: 5613 OOOOOPPPPP 139693508122368 pid: 5613 EEEEEEEEE 139693497632512 ...
能够看到在 Python 内里输出的线程名就是我们设置的那样,然则 Ps 的效果倒是令我疑心人生:
root@10-46-33-56:~# ps -Tp 5613 PID SPID TTY TIME CMD 5613 5613 pts/2 00:00:00 python 5613 5614 pts/2 00:00:00 python 5613 5615 pts/2 00:00:00 python
一般来讲不该是如许呀,我有点迷了,岂非我一向都是记错了?用别的言语版本的多线程来测试下:
C 版本的多线程
#include<stdio.h> #include<sys/syscall.h> #include<sys/prctl.h> #include<pthread.h> void *test(void *name) { pid_t pid, tid; pid = getpid(); tid = syscall(__NR_gettid); char *tname = (char *)name; // 设置线程名字 prctl(PR_SET_NAME, tname); while(1) { printf("pid: %d, thread_id: %u, t_name: %s\n", pid, tid, tname); sleep(3); } } int main() { pthread_t t1, t2; void *ret; pthread_create(&t1, NULL, test, (void *)"Love_test_1"); pthread_create(&t2, NULL, test, (void *)"Love_test_2"); pthread_join(t1, &ret); pthread_join(t2, &ret); }
输出:
root@10-46-33-56:~# gcc t.c -lpthread && ./a.out pid: 5575, thread_id: 5577, t_name: Love_test_2 pid: 5575, thread_id: 5576, t_name: Love_test_1 pid: 5575, thread_id: 5577, t_name: Love_test_2 pid: 5575, thread_id: 5576, t_name: Love_test_1 ...
用 PS 敕令再次考证:
root@10-46-33-56:~# ps -Tp 5575 PID SPID TTY TIME CMD 5575 5575 pts/2 00:00:00 a.out 5575 5576 pts/2 00:00:00 Love_test_1 5575 5577 pts/2 00:00:00 Love_test_2
这个才是准确嘛,线程名确实是能够经由过程 Ps 看出来的嘛!
不过为啥 Python 谁人看不到呢?既然是经由过程 setName
设置线程名的,那就看看定义咯:
[threading.py] class Thread(_Verbose): ... @property def name(self): """A string used for identification purposes only. It has no semantics. Multiple threads may be given the same name. The initial name is set by the constructor. """ assert self.__initialized, "Thread.__init__() not called" return self.__name @name.setter def name(self, name): assert self.__initialized, "Thread.__init__() not called" self.__name = str(name) def setName(self, name): self.name = name ...
看到这里实在只是在 Thread
对象的属性设置了罢了,并没有动到基础,那一定就是看不到咯~
如许看起来,我们已没方法经由过程 ps
或许 /proc/
这类手腕在外部搜刮 python 线程名了,所以我们只能在 Python 内部来处理。
因而题目就变成了,怎样在 Python 内部拿到一切正在运转的线程呢?
threading.enumerate
能够圆满处理这个题目!Why?
Because 在下面这个函数的 doc 内里说得很清晰了,返回一切活泼的线程对象,不包括住手和未启动的。
[threading.py] def enumerate(): """Return a list of all Thread objects currently alive. The list includes daemonic threads, dummy thread objects created by current_thread(), and the main thread. It excludes terminated threads and threads that have not yet been started. """ with _active_limbo_lock: return _active.values() + _limbo.values()
由于拿到的是 Thread 的对象,所以我们经由过程这个能到该线程相干的信息!
请看完全代码示例:
#coding: utf8 import threading import os import time def get_thread(): pid = os.getpid() while True: ts = threading.enumerate() print '------- Running threads On Pid: %d -------' % pid for t in ts: print t.name, t.ident print time.sleep(1) def tt(): info = threading.currentThread() pid = os.getpid() while True: print 'pid: {}, tid: {}, tname: {}'.format(pid, info.name, info.ident) time.sleep(3) return t1 = threading.Thread(target=tt) t1.setName('Thread-test1') t1.setDaemon(True) t1.start() t2 = threading.Thread(target=tt) t2.setName('Thread-test2') t2.setDaemon(True) t2.start() t3 = threading.Thread(target=get_thread) t3.setName('Checker') t3.setDaemon(True) t3.start() t1.join() t2.join() t3.join()
输出:
root@10-46-33-56:~# python t_show.py pid: 6258, tid: Thread-test1, tname: 139907597162240 pid: 6258, tid: Thread-test2, tname: 139907586672384 ------- Running threads On Pid: 6258 ------- MainThread 139907616806656 Thread-test1 139907597162240 Checker 139907576182528 Thread-test2 139907586672384 ------- Running threads On Pid: 6258 ------- MainThread 139907616806656 Thread-test1 139907597162240 Checker 139907576182528 Thread-test2 139907586672384 ------- Running threads On Pid: 6258 ------- MainThread 139907616806656 Thread-test1 139907597162240 Checker 139907576182528 Thread-test2 139907586672384 ------- Running threads On Pid: 6258 ------- MainThread 139907616806656 Checker 139907576182528 ...
代码看起来有点长,然则逻辑相称简朴,Thread-test1
和 Thread-test2
都是打印出当前的 pid、线程 id 和 线程名字,然后 3s 后退出,这个是想模仿线程一般退出。
而 Checker
线程则是每秒经由过程 threading.enumerate
输出当前历程内一切活泼的线程。
能够显著看到一最先是能够看到 Thread-test1
和 Thread-test2
的信息,当它俩退出以后就只剩下 MainThread
和 Checker
自身罢了了。
烧毁指定线程
既然能拿到名字和线程 id,那我们也就能干掉指定的线程了!
假定如今 Thread-test2
已黑化,发疯了,我们须要阻止它,那我们就能够经由过程这类体式格局处理了:
在上面的代码基础上,增添和补上以下代码:
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: ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, None) raise SystemError("PyThreadState_SetAsyncExc failed") def stop_thread(thread): _async_raise(thread.ident, SystemExit) def get_thread(): pid = os.getpid() while True: ts = threading.enumerate() print '------- Running threads On Pid: %d -------' % pid for t in ts: print t.name, t.ident, t.is_alive() if t.name == 'Thread-test2': print 'I am go dying! Please take care of yourself and drink more hot water!' stop_thread(t) print time.sleep(1)
输出
root@10-46-33-56:~# python t_show.py pid: 6362, tid: 139901682108160, tname: Thread-test1 pid: 6362, tid: 139901671618304, tname: Thread-test2 ------- Running threads On Pid: 6362 ------- MainThread 139901706389248 True Thread-test1 139901682108160 True Checker 139901661128448 True Thread-test2 139901671618304 True Thread-test2: I am go dying. Please take care of yourself and drink more hot water! ------- Running threads On Pid: 6362 ------- MainThread 139901706389248 True Thread-test1 139901682108160 True Checker 139901661128448 True Thread-test2 139901671618304 True Thread-test2: I am go dying. Please take care of yourself and drink more hot water! pid: 6362, tid: 139901682108160, tname: Thread-test1 ------- Running threads On Pid: 6362 ------- MainThread 139901706389248 True Thread-test1 139901682108160 True Checker 139901661128448 True // Thread-test2 已不在了
一顿操纵下来,虽然我们如许看待 Thread-test2
,但它照样体贴着我们:多喝热水,
PS: 热水虽好,八杯足矣,请勿贪酒哦。
书回正传,上述的要领是极为粗犷的,为何这么说呢?
由于它的道理是:应用 Python 内置的 API,触发指定线程的非常,让其能够自动退出;
万不得已真不要用这类要领,有一定概率触发不可形貌的题目。牢记!别问我为何会晓得...
为何住手线程这么难
多线程自身设想就是在历程下的合作并发,是调理的最小单位,线程间分食着历程的资本,所以会有许多锁机制和状况掌握。
假如运用强制手腕干掉线程,那末很大概率涌现意想不到的bug。 而且最主要的锁资本开释能够也会涌现意想不到题目。
我们以至也没法经由过程信号杀死历程那样直接杀线程,由于 kill 只要应付历程才到达我们的预期,而应付线程显著不能够,不论杀哪一个线程,全部历程都邑退出!
而由于有 GIL,使得许多童鞋都以为 Python 的线程是Python 自行完成出来的,并不是现实存在,Python 应当能够直接烧毁吧?
然则事实上 Python 的线程都是名副实在的线程!
什么意思呢?Python 的线程是操纵体系经由过程 pthread 建立的原生线程。Python 只是经由过程 GIL 来束缚这些线程,来决议什么时刻最先调理,比方说运转了多少个指令就交出 GIL,至于谁夺得花魁,得听操纵体系的。
假如是纯真的线程,实在体系是有方法住手的,比方: pthread_exit
,pthread_kill
或 pthread_cancel
, 概况可看:https://www.cnblogs.com/Creat...
很可惜的是: Python 层面并没有这些要领的封装!我的天,好气!能够人家以为,线程就该温顺看待吧。
怎样温顺退出线程
想要温顺退出线程,实在差不多就是一句空话了~
要么运转完退出,要么设置标志位,经常搜检标记位,该退出的就退出咯。
扩大
《怎样准确的住手正在运转的子线程》:https://www.cnblogs.com/Creat...
《不要粗犷的烧毁python线程》:http://xiaorui.cc/2017/02/22/...
迎接列位大神指导交换, QQ议论群: 258498217
转载请说明泉源: https://segmentfault.com/a/11...
保存一切权益
告发
假如以为我的文章对你有效,请随便赞扬
你能够感兴趣的
2 条批评
默许排序 时候排序
舞林 · 1 天前
假如是我能够kill -9了,宁肯错杀一千,不可放过一个,蛤蛤
赞 复兴
0
不可呀~ -9 历程里全死了~
— Lin_R 作者 · 1 天前
增加复兴
载入中...
显现更多批评
以上就是Python线程中定位与烧毁的细致引见(附示例)的细致内容,更多请关注ki4网别的相干文章!