国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Python:線程之定位與銷毀

Heier / 1363人閱讀

摘要:而線程則是每秒通過(guò)輸出當(dāng)前進(jìn)程內(nèi)所有活躍的線程。如果使用強(qiáng)制手段干掉線程,那么很大幾率出現(xiàn)意想不到的。只是通過(guò)來(lái)約束這些線程,來(lái)決定什么時(shí)候開始調(diào)度,比方說(shuō)運(yùn)行了多少個(gè)指令就交出,至于誰(shuí)奪得花魁,得聽操作系統(tǒng)的。

背景

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

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

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

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

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

找出需要清理的線程號(hào) tid;

銷毀它們;

找出線程ID

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

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 里面輸出的線程名就是我們?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ǔ)言版本的多線程來(lái)測(cè)試下:

C 版本的多線程

#include
#include
#include
#include

void *test(void *name)
{    
    pid_t pid, tid;
    pid = getpid();
    tid = syscall(__NR_gettid);
    char *tname = (char *)name;
    
    // 設(shè)置線程名字
    prctl(PR_SET_NAME, tname);
    
    while(1)
    {
        printf("pid: %d, thread_id: %u, t_name: %s
", 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è)才是正確嘛,線程名確實(shí)是可以通過(guò) Ps 看出來(lái)的嘛!

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

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

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

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

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

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

Because 在下面這個(gè)函數(shù)的 doc 里面說(shuō)得很清楚了,返回所有活躍的線程對(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è)能到該線程相關(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、線程 id 和 線程名字,然后 3s 后退出,這個(gè)是想模擬線程正常退出。

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

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

銷毀指定線程

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

假設(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)勿貪杯哦。

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

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

萬(wàn)不得已真不要用這種方法,有一定概率觸發(fā)不可描述的問(wèn)題。切記!別問(wèn)我為什么會(huì)知道...

為什么停止線程這么難

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

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

我們甚至也無(wú)法通過(guò)信號(hào)殺死進(jìn)程那樣直接殺線程,因?yàn)?kill 只有對(duì)付進(jìn)程才能達(dá)到我們的預(yù)期,而對(duì)付線程明顯不可以,不管殺哪個(gè)線程,整個(gè)進(jìn)程都會(huì)退出!

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

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

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

如果是單純的線程,其實(shí)系統(tǒng)是有辦法終止的,比如: pthread_exit,pthread_killpthread_cancel, 詳情可看:https://www.cnblogs.com/Creat...

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

如何溫柔退出線程

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

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

擴(kuò)展

《如何正確的終止正在運(yùn)行的子線程》:https://www.cnblogs.com/Creat...
《不要粗暴的銷毀python線程》:http://xiaorui.cc/2017/02/22/...

歡迎各位大神指點(diǎn)交流, QQ討論群: 258498217
轉(zhuǎn)載請(qǐng)注明來(lái)源: https://segmentfault.com/a/11...

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/43192.html

相關(guān)文章

  • python

    Python裝飾器為什么難理解? 無(wú)論項(xiàng)目中還是面試都離不開裝飾器話題,裝飾器的強(qiáng)大在于它能夠在不修改原有業(yè)務(wù)邏輯的情況下對(duì)代碼進(jìn)行擴(kuò)展,權(quán)限校驗(yàn)、用戶認(rèn)證、日志記錄、性能測(cè)試、事務(wù)處理、緩存等都是裝飾器的絕佳應(yīng)用場(chǎng)景,它能夠最大程度地對(duì)代碼進(jìn)行復(fù)用。 但為什么初學(xué)者對(duì)裝飾器的理解如此困難,我認(rèn)為本質(zhì)上是對(duì)Py… Python 實(shí)現(xiàn)車牌定位及分割 作者用 Python 實(shí)現(xiàn)車牌定位及分割的實(shí)踐。 ...

    chenatu 評(píng)論0 收藏0
  • Python

    摘要:最近看前端都展開了幾場(chǎng)而我大知乎最熱語(yǔ)言還沒有相關(guān)。有關(guān)書籍的介紹,大部分截取自是官方介紹。但從開始,標(biāo)準(zhǔn)庫(kù)為我們提供了模塊,它提供了和兩個(gè)類,實(shí)現(xiàn)了對(duì)和的進(jìn)一步抽象,對(duì)編寫線程池進(jìn)程池提供了直接的支持。 《流暢的python》閱讀筆記 《流暢的python》是一本適合python進(jìn)階的書, 里面介紹的基本都是高級(jí)的python用法. 對(duì)于初學(xué)python的人來(lái)說(shuō), 基礎(chǔ)大概也就夠用了...

    dailybird 評(píng)論0 收藏0
  • 通讀Python官方文檔Threading

    摘要:但現(xiàn)在線程沒有優(yōu)先級(jí),沒有線程組,不能被銷毀停止暫停開始和打斷。守護(hù)線程也會(huì)結(jié)束,并強(qiáng)行終止整個(gè)程序。在中,他是目前可用的最底層的同步原語(yǔ),由模塊提供。當(dāng)處于狀態(tài)時(shí),方法可以將狀態(tài)變?yōu)椋⒘⒓捶祷亍7駝t會(huì)拋出錯(cuò)誤。對(duì)象實(shí)現(xiàn)某些服務(wù)的共進(jìn)退。 Python的threading模塊松散地基于Java的threading模塊。但現(xiàn)在線程沒有優(yōu)先級(jí),沒有線程組,不能被銷毀、停止、暫停、開始和打...

    jasperyang 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<