欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Python線(xiàn)程之定位與銷(xiāo)毀的實(shí)現(xiàn)

 更新時(shí)間:2019年02月17日 09:31:10   作者:Lin_R  
這篇文章主要介紹了Python線(xiàn)程之定位與銷(xiāo)毀的實(shí)現(xiàn),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧

背景

開(kāi)工前我就覺(jué)得有什么不太對(duì)勁,感覺(jué)要背鍋。這可不,上班第三天就捅鍋了。

我們有個(gè)了不起的后臺(tái)程序,可以動(dòng)態(tài)加載模塊,并以線(xiàn)程方式運(yùn)行,通過(guò)這種形式實(shí)現(xiàn)插件的功能。而模塊更新時(shí)候,后臺(tái)程序自身不會(huì)退出,只會(huì)將模塊對(duì)應(yīng)的線(xiàn)程關(guān)閉、更新代碼再啟動(dòng),6 得不行。

于是乎我就寫(xiě)了個(gè)模塊準(zhǔn)備大展身手,結(jié)果忘記寫(xiě)退出函數(shù)了,導(dǎo)致每次更新模塊都新創(chuàng)建一個(gè)線(xiàn)程,除非重啟那個(gè)程序,否則那些線(xiàn)程就一直茍活著。

這可不行啊,得想個(gè)辦法清理呀,要不然怕是要炸了。

那么怎么清理呢?我能想到的就是兩步走:

  • 找出需要清理的線(xiàn)程號(hào) tid;
  • 銷(xiāo)毀它們;

找出線(xiàn)程ID

和平時(shí)的故障排查相似,先通過(guò) ps 命令看看目標(biāo)進(jìn)程的線(xiàn)程情況,因?yàn)橐呀?jīng)是 setName 設(shè)置過(guò)線(xiàn)程名,所以正常來(lái)說(shuō)應(yīng)該是看到對(duì)應(yīng)的線(xiàn)程的。 直接用下面代碼來(lái)模擬這個(gè)線(xiàn)程:

Python 版本的多線(xiàn)程

#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 里面輸出的線(xiàn)程名就是我們?cè)O(shè)置的那樣,然而 Ps 的結(jié)果卻是令我懷疑人生:

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

正常來(lái)說(shuō)不該是這樣呀,我有點(diǎn)迷了,難道我一直都是記錯(cuò)了?用別的語(yǔ)言版本的多線(xiàn)程來(lái)測(cè)試下:

C 版本的多線(xiàn)程

#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;
  
  // 設(shè)置線(xiàn)程名字
  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 命令再次驗(yàn)證:

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

這個(gè)才是正確嘛,線(xiàn)程名確實(shí)是可以通過(guò) Ps 看出來(lái)的嘛!

不過(guò)為啥 Python 那個(gè)看不到呢?既然是通過(guò) setName 設(shè)置線(xiàn)程名的,那就看看定義咯:

[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
  def setName(self, name):
    self.name = name
  ...

看到這里其實(shí)只是在 Thread 對(duì)象的屬性設(shè)置了而已,并沒(méi)有動(dòng)到根本,那肯定就是看不到咯~

這樣看起來(lái),我們已經(jīng)沒(méi)辦法通過(guò) ps 或者 /proc/ 這類(lèi)手段在外部搜索 python 線(xiàn)程名了,所以我們只能在 Python 內(nèi)部來(lái)解決。

于是問(wèn)題就變成了,怎樣在 Python 內(nèi)部拿到所有正在運(yùn)行的線(xiàn)程呢?

threading.enumerate 可以完美解決這個(gè)問(wèn)題!Why?

Because 在下面這個(gè)函數(shù)的 doc 里面說(shuō)得很清楚了,返回所有活躍的線(xiàn)程對(duì)象,不包括終止和未啟動(dòng)的。

[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()

因?yàn)槟玫降氖?Thread 的對(duì)象,所以我們通過(guò)這個(gè)能到該線(xiàn)程相關(guān)的信息!

請(qǐng)看完整代碼示例:

#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
...

代碼看起來(lái)有點(diǎn)長(zhǎng),但是邏輯相當(dāng)簡(jiǎn)單,Thread-test1Thread-test2 都是打印出當(dāng)前的 pid、線(xiàn)程 id 和 線(xiàn)程名字,然后 3s 后退出,這個(gè)是想模擬線(xiàn)程正常退出。

Checker 線(xiàn)程則是每秒通過(guò) threading.enumerate 輸出當(dāng)前進(jìn)程內(nèi)所有活躍的線(xiàn)程。

可以明顯看到一開(kāi)始是可以看到 Thread-test1Thread-test2的信息,當(dāng)它倆退出之后就只剩下 MainThreadChecker 自身而已了。

銷(xiāo)毀指定線(xiàn)程

既然能拿到名字和線(xiàn)程 id,那我們也就能干掉指定的線(xiàn)程了!

假設(shè)現(xiàn)在 Thread-test2 已經(jīng)黑化,發(fā)瘋了,我們需要制止它,那我們就可以通過(guò)這種方式解決了:

在上面的代碼基礎(chǔ)上,增加和補(bǔ)上下列代碼:

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 已經(jīng)不在了

一頓操作下來(lái),雖然我們這樣對(duì)待 Thread-test2,但它還是關(guān)心著我們:多喝熱水,

PS: 熱水雖好,八杯足矣,請(qǐng)勿貪杯哦。

書(shū)回正傳,上述的方法是極為粗暴的,為什么這么說(shuō)呢?

因?yàn)樗脑硎牵豪?Python 內(nèi)置的 API,觸發(fā)指定線(xiàn)程的異常,讓其可以自動(dòng)退出;

為什么停止線(xiàn)程這么難

多線(xiàn)程本身設(shè)計(jì)就是在進(jìn)程下的協(xié)作并發(fā),是調(diào)度的最小單元,線(xiàn)程間分食著進(jìn)程的資源,所以會(huì)有許多鎖機(jī)制和狀態(tài)控制。

如果使用強(qiáng)制手段干掉線(xiàn)程,那么很大幾率出現(xiàn)意想不到的bug。 而且最重要的鎖資源釋放可能也會(huì)出現(xiàn)意想不到問(wèn)題。

而因?yàn)橛?GIL,使得很多童鞋都覺(jué)得 Python 的線(xiàn)程是Python 自行實(shí)現(xiàn)出來(lái)的,并非實(shí)際存在,Python 應(yīng)該可以直接銷(xiāo)毀吧?

然而事實(shí)上 Python 的線(xiàn)程都是貨真價(jià)實(shí)的線(xiàn)程!

什么意思呢?Python 的線(xiàn)程是操作系統(tǒng)通過(guò) pthread 創(chuàng)建的原生線(xiàn)程。Python 只是通過(guò) GIL 來(lái)約束這些線(xiàn)程,來(lái)決定什么時(shí)候開(kāi)始調(diào)度,比方說(shuō)運(yùn)行了多少個(gè)指令就交出 GIL,至于誰(shuí)奪得花魁,得聽(tīng)操作系統(tǒng)的。

如果是單純的線(xiàn)程,其實(shí)系統(tǒng)是有辦法終止的,比如: pthread_exit,pthread_killpthread_cancel, 詳情可看:http://www.dbjr.com.cn/article/156412.htm

很可惜的是: Python 層面并沒(méi)有這些方法的封裝!我的天,好氣!可能人家覺(jué)得,線(xiàn)程就該溫柔對(duì)待吧。

如何溫柔退出線(xiàn)程

想要溫柔退出線(xiàn)程,其實(shí)差不多就是一句廢話(huà)了~

要么運(yùn)行完退出,要么設(shè)置標(biāo)志位,時(shí)常檢查標(biāo)記位,該退出的就退出咯。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • python實(shí)現(xiàn)高斯判別分析算法的例子

    python實(shí)現(xiàn)高斯判別分析算法的例子

    今天小編就為大家分享一篇python實(shí)現(xiàn)高斯判別分析算法的例子,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2019-12-12
  • 利用python實(shí)現(xiàn)在微信群刷屏的方法

    利用python實(shí)現(xiàn)在微信群刷屏的方法

    今天小編就為大家分享一篇利用python實(shí)現(xiàn)在微信群刷屏的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2019-02-02
  • python IDLE添加行號(hào)顯示教程

    python IDLE添加行號(hào)顯示教程

    這篇文章主要介紹了python IDLE添加行號(hào)顯示教程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-04-04
  • Python教程之無(wú)限迭代器的使用詳解

    Python教程之無(wú)限迭代器的使用詳解

    Python的Itetool是一個(gè)模塊,它提供了各種函數(shù),這些函數(shù)在迭代器上工作以產(chǎn)生復(fù)雜的迭代器。該模塊作為一個(gè)快速,內(nèi)存效率的工具,可以單獨(dú)使用或組合使用以形成迭代器代數(shù)。本文就來(lái)和大家詳細(xì)聊聊無(wú)限迭代器,感興趣的可以了解一下
    2022-09-09
  • 利用django-suit模板添加自定義的菜單、頁(yè)面及設(shè)置訪(fǎng)問(wèn)權(quán)限

    利用django-suit模板添加自定義的菜單、頁(yè)面及設(shè)置訪(fǎng)問(wèn)權(quán)限

    這篇文章主要給大家介紹了關(guān)于利用django-suit模板添加自定義的菜單、頁(yè)面及設(shè)置訪(fǎng)問(wèn)權(quán)限的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起看看吧
    2018-07-07
  • Python descriptor(描述符)的實(shí)現(xiàn)

    Python descriptor(描述符)的實(shí)現(xiàn)

    這篇文章主要介紹了Python descriptor(描述符)的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-11-11
  • Python四大金剛之集合詳解

    Python四大金剛之集合詳解

    這篇文章主要介紹了Python的集合,小編覺(jué)得這篇文章寫(xiě)的還不錯(cuò),需要的朋友可以參考下,希望能夠給你帶來(lái)幫助
    2021-10-10
  • 如何利用python寫(xiě)GUI及生成.exe可執(zhí)行文件

    如何利用python寫(xiě)GUI及生成.exe可執(zhí)行文件

    工作中需要開(kāi)發(fā)一個(gè)小工具,簡(jiǎn)單的UI界面可以很好的提高工具的實(shí)用性,由此開(kāi)啟了我的第一次GUI開(kāi)發(fā)之旅,這篇文章主要給大家介紹了關(guān)于如何利用python寫(xiě)GUI及生成.exe可執(zhí)行文件的相關(guān)資料,需要的朋友可以參考下
    2021-12-12
  • Python多線(xiàn)程同步Lock、RLock、Semaphore、Event實(shí)例

    Python多線(xiàn)程同步Lock、RLock、Semaphore、Event實(shí)例

    這篇文章主要介紹了Python多線(xiàn)程同步Lock、RLock、Semaphore、Event實(shí)例,Lock & RLock 用來(lái)確保多線(xiàn)程多共享資源的訪(fǎng)問(wèn),Semaphore用來(lái)確保一定資源多線(xiàn)程訪(fǎng)問(wèn)時(shí)的上限,Event是最簡(jiǎn)單的線(xiàn)程間通信的方式,需要的朋友可以參考下
    2014-11-11
  • python 將日期戳(五位數(shù)時(shí)間)轉(zhuǎn)換為標(biāo)準(zhǔn)時(shí)間

    python 將日期戳(五位數(shù)時(shí)間)轉(zhuǎn)換為標(biāo)準(zhǔn)時(shí)間

    這篇文章主要介紹了python 將日期戳(五位數(shù)時(shí)間)轉(zhuǎn)換為標(biāo)準(zhǔn)時(shí)間的實(shí)現(xiàn)方法,本文圖文并茂給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值 ,需要的朋友可以參考下
    2019-07-07

最新評(píng)論