>  기사  >  백엔드 개발  >  Python 스레드의 위치 지정 및 소멸에 대한 자세한 소개(예제 포함)

Python 스레드의 위치 지정 및 소멸에 대한 자세한 소개(예제 포함)

不言
不言앞으로
2019-02-20 14:41:522428검색

이 문서는 Python 스레드의 위치 지정 및 파괴에 대한 자세한 소개를 제공합니다(예제 포함). 필요한 친구가 참조할 수 있기를 바랍니다.

작업을 시작하기 전부터 뭔가 잘못된 느낌이 들었고, 책임을 져야 할 것 같은 기분이 들었습니다. 아니요, 출근 3일째인데 죄책감이 듭니다.

모듈을 동적으로 로드하고 스레드에서 실행할 수 있는 놀라운 백그라운드 프로그램이 있습니다. 이러한 방식으로 플러그인의 기능이 실현됩니다. 모듈이 업데이트되면 백그라운드 프로그램 자체는 종료되지 않습니다. 모듈에 해당하는 스레드만 닫고 코드를 업데이트한 다음 시작할 수 없습니다.

그래서 실력을 뽐내기 위해 모듈을 작성했는데 종료 기능을 작성하는 것을 잊어버렸기 때문에 모듈을 업데이트할 때마다 새로운 스레드가 생성되었습니다. 프로그램을 다시 시작하지 않으면 해당 스레드는 계속 유지됩니다.

이건 불가능합니다. 청소할 방법을 찾아야 합니다. 그렇지 않으면 폭발할까봐 두렵습니다.

그럼 어떻게 청소하나요? 제가 생각할 수 있는 것은 두 단계뿐입니다.

  1. 청소해야 할 스레드 번호 tid를 찾아보세요.

  2. 제거하세요.

스레드 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의 결과를 보면 내 생각이 의심스럽습니다. life :

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>
#include<sys>
#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);
}</pthread.h></sys></sys></stdio.h>

출력:

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을 통해 설정되므로 정의를 살펴보겠습니다. 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-test1Thread-test2 都是打印出当前的 pid、线程 id 和 线程名字,然后 3s 后退出,这个是想模拟线程正常退出。

Checker 线程则是每秒通过 threading.enumerate 输出当前进程内所有活跃的线程。

可以明显看到一开始是可以看到 Thread-test1Thread-test2的信息,当它俩退出之后就只剩下 MainThreadChecker 自身而已了。

销毁指定线程

既然能拿到名字和线程 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

#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()
여기서 보는 것은 단지 Thread 개체의 속성을 설정한다는 것입니다. , 기본적으로 보이지 않아야 합니다~더 이상 ps 또는 /proc/ 이므로 Python 내에서만 해결할 수 있습니다. <p></p>그러므로 질문은 Python 내부에서 실행 중인 모든 스레드를 어떻게 얻을 수 있는가 하는 것입니다. <p></p> <code>threading.enumerate를 사용하면 이 문제를 완벽하게 해결할 수 있습니다! 왜요?

아래 함수 문서에 명확하게 명시되어 있기 때문에 종료되거나 시작되지 않은 개체를 제외하고 모든 활성

스레드 개체를 반환합니다. Python 스레드의 위치 지정 및 소멸에 대한 자세한 소개(예제 포함)

root@10-46-33-56:~# python t.py
pid:  5613
OOOOOPPPPP 139693508122368
pid:  5613
EEEEEEEEE 139693497632512
...
Thread의 객체를 가져오기 때문에, 이를 통해 Thread에 관련된 정보를 얻을 수 있어요!

전체 코드 예를 참조하세요.

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

출력:

#include<stdio.h>
#include<sys>
#include<sys>
#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);
}</pthread.h></sys></sys></stdio.h>

코드가 조금 길어 보이지만 논리는 Thread-test1Thread-test2 현재 pid, 스레드 ID, 스레드 이름을 출력하고 3초 후에 종료합니다. 이는 스레드의 정상적인 종료를 시뮬레이션하기 위한 것입니다.

Checker 스레드는 매초 threading.enumerate를 통해 현재 프로세스의 모든 활성 스레드를 출력합니다.

🎜처음에 Thread-test1Thread-test2의 정보를 확인할 수 있습니다. 종료 후에는 MainThread 및 <code>Checker는 그 자체입니다. 🎜🎜지정된 스레드 삭제🎜🎜이제 이름과 스레드 ID를 얻을 수 있으므로 지정된 스레드도 삭제할 수 있습니다! 🎜🎜이제 Thread-test2가 어두워지고 이상해져서 이를 중지해야 한다고 가정하면 다음과 같이 해결할 수 있습니다. 🎜🎜위 코드를 기반으로 다음을 추가하고 추가합니다. 다음 코드: 🎜
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
...
🎜Output🎜
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
🎜한 번의 작업 후에 Thread-test2를 이와 같이 처리하더라도 여전히 우리에 대해 관심을 갖습니다. 🎜Drink more hot water🎜, 🎜🎜PS: Hot water But 괜찮아요 여덟 잔이면 충분하니까 욕심내지 마세요. 🎜🎜본론으로 돌아가서, 위의 방법은 매우 조잡합니다. 왜 그렇게 말합니까? 🎜🎜원칙은 다음과 같습니다. Python의 내장 API를 사용하여 지정된 스레드에서 예외를 발생시켜 자동으로 종료할 수 있도록 합니다. 🎜🎜🎜🎜🎜🎜🎜이 방법을 최후의 수단으로 사용하지 마십시오. 형언할 수 없는 문제를 일으킬 확률. 기억하다! 왜 아는지는 묻지 마세요...🎜🎜스레드를 중지하는 것이 왜 그렇게 어려운가요?🎜🎜멀티스레딩 자체는 프로세스 하에서 협업적 동시성을 위해 설계되었습니다. 스레드는 리소스를 공유하는 가장 작은 단위입니다. 프로세스의 메커니즘과 상태 제어가 많습니다. 🎜🎜 스레드를 강제로 죽이는 경우 예상치 못한 버그가 발생할 가능성이 높습니다. 그리고 가장 중요한 잠금 리소스 해제로 인해 예상치 못한 문제가 발생할 수도 있습니다. 🎜

신호를 통해 프로세스를 종료하는 것처럼 스레드를 직접 종료할 수도 없습니다. 왜냐하면 kill은 프로세스를 처리함으로써만 우리의 기대를 달성할 수 있기 때문입니다. 그러나 스레드를 처리하는 것은 분명히 불가능합니다. !

그리고 GIL 때문에 많은 아이들은 Python 스레드가 Python 자체로 구현되어 실제로 존재하지 않는다고 생각합니다. Python을 직접 파괴해야 합니다. 그렇죠?

그러나 사실 Python의 스레드는 실제 스레드입니다!

무슨 뜻인가요? Python 스레드는 운영 체제에서 pthread를 통해 생성된 기본 스레드입니다. Python은 예약을 시작할 시기를 결정하기 위해 이러한 스레드를 제한하는 데에만 GIL을 사용합니다.

간단한 스레드인 경우 시스템에서는 실제로 이를 종료할 수 있는 방법이 있습니다. 예: pthread_exit,pthread_kill  或 pthread_cancel, 자세한 내용은 https://www.cnblogs.com/Creat를 참조하세요...

안타깝습니다. : Python 레벨에는 이러한 메소드 캡슐화가 없습니다! 맙소사, 너무 화가 났어요! 어쩌면 사람들은 실을 부드럽게 다루어야 한다고 생각할 수도 있습니다.

스레드를 부드럽게 종료하는 방법

스레드를 부드럽게 종료하려면 말도 안 되는 소리에 가깝습니다~

실행 후 종료하거나, 플래그 비트를 설정하고, 플래그 비트를 자주 확인하고, 때가 되면 종료하세요. 출구.

Extension

"실행 중인 하위 스레드를 올바르게 종료하는 방법": https://www.cnblogs.com/Creat...
"파이썬 스레드를 대충 파괴하지 마세요": http://xiaorui.cc/2017 /02/22/...

모든 전문가의 조언과 교류를 환영합니다. QQ 토론 그룹: 258498217
재인쇄할 출처를 표시해주세요: https://segmentfault.com/a/11...







C

linux

                                                                         266번 읽음                                                        읽는 데 30분 정도 걸립니다                                                                          -                                   



    ~                          -                                                                                                                                                                                                                                                                                                                                           前 배경

    작업을 시작하기 전부터 옳지 않다는 생각이 들어서 들고 가고 싶은 마음이 듭니다. 아니요, 출근 3일째인데 죄책감이 듭니다. 모듈을 동적으로 로드하고 스레드 방식으로 실행할 수 있는 놀라운 백그라운드 프로그램이 있습니다. 이러한 방식으로 플러그인의 기능이 실현됩니다. 모듈이 업데이트되면 백그라운드 프로그램 자체는 종료되지 않습니다. 모듈에 해당하는 스레드만 닫고 코드를 업데이트한 다음 시작할 수 없습니다. 그래서 실력을 뽐내기 위해 모듈을 작성했는데 종료 기능을 작성하는 것을 잊어버렸기 때문에 모듈을 업데이트할 때마다 새로운 스레드가 생성되었습니다. 프로그램을 다시 시작하지 않으면 해당 스레드는 계속 유지됩니다. 이건 불가능합니다. 청소할 방법을 찾아야 합니다. 그렇지 않으면 폭발할까봐 두렵습니다. 그럼 어떻게 청소하나요? 제가 생각할 수 있는 것은 두 단계뿐입니다. 청소해야 할 스레드 번호 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의 결과를 보면 내 생각이 의심스럽습니다. life :

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
    보통은 이러면 안 되는데, 제가 계속 잘못 기억한 걸까요? 다른 언어 버전의 멀티스레딩을 사용하여 테스트:
  1. 멀티스레딩의 C 버전

    #include<stdio.h>
    #include<sys>
    #include<sys>
    #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);
    }</pthread.h></sys></sys></stdio.h>
  2. 출력:
  3. 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
  4. 스레드 이름이 실제로 볼 수 있습니까? 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 내부에서 실행 중인 모든 스레드를 어떻게 얻을 수 있는가 하는 것입니다.

threading.enumerate를 사용하면 이 문제를 완벽하게 해결할 수 있습니다! 왜요?

아래 이 함수 문서에 명확하게 명시되어 있기 때문에 종료되거나 시작되지 않은 개체를 제외하고 모든 활성

스레드 개체

를 반환합니다.

[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 객체를 가져오기 때문에 이를 통해 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-test1Thread-test2 현재 pid, 스레드 ID, 스레드 이름을 출력하고 3초 후에 종료합니다. 이는 스레드의 정상적인 종료를 시뮬레이션하기 위한 것입니다. <code>setName 设置线程名的,那就看看定义咯:

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)

看到这里其实只是在 Thread 对象的属性设置了而已,并没有动到根本,那肯定就是看不到咯~

这样看起来,我们已经没办法通过 ps 或者 /proc/ 这类手段在外部搜索 python 线程名了,所以我们只能在 Python 内部来解决。

于是问题就变成了,怎样在 Python 内部拿到所有正在运行的线程呢?

threading.enumerate  可以完美解决这个问题!Why?

Because 在下面这个函数的 doc 里面说得很清楚了,返回所有活跃的线程对象,不包括终止和未启动的。

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 的对象,所以我们通过这个能到该线程相关的信息!

请看完整代码示例:

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

代码看起来有点长,但是逻辑相当简单,Thread-test1Thread-test2 都是打印出当前的 pid、线程 id 和 线程名字,然后 3s 后退出,这个是想模拟线程正常退出。

Checker 线程则是每秒通过 threading.enumerate 输出当前进程内所有活跃的线程。

可以明显看到一开始是可以看到 Thread-test1Thread-test2的信息,当它俩退出之后就只剩下 MainThreadChecker 自身而已了。

销毁指定线程

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

假设现在 Thread-test2 已经黑化,发疯了,我们需要制止它,那我们就可以通过这种方式解决了:

在上面的代码基础上,增加和补上下列代码:

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

输出

#include<stdio.h>
#include<sys>
#include<sys>
#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);
}</pthread.h></sys></sys></stdio.h>

一顿操作下来,虽然我们这样对待 Thread-test2Checker 스레드는 매초 threading.enumerate를 통해 현재 프로세스의 모든 활성 스레드를 출력합니다.

처음에는 Thread-test1Thread-test2의 정보를 확인할 수 있습니다. 종료 후에는 MainThread와 <code>Checker는 그 자체입니다.

지정된 스레드 삭제🎜🎜이제 이름과 스레드 ID를 얻을 수 있으므로 지정된 스레드도 삭제할 수 있습니다! 🎜🎜이제 Thread-test2가 어두워지고 이상해져서 이를 중지해야 한다고 가정하면 다음과 같이 해결할 수 있습니다. 🎜🎜위 코드를 기반으로 다음을 추가하고 추가합니다. 다음 코드: 🎜
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
...
🎜Output🎜
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
🎜한 번의 작업 후에 Thread-test2를 이와 같이 처리하더라도 여전히 우리에 대해 관심을 갖습니다. 🎜Drink more hot water🎜, 🎜🎜PS: Hot water But 괜찮아요 여덟 잔이면 충분하니까 욕심내지 마세요. 🎜

본론으로 돌아가서, 위의 방법은 매우 조잡합니다. 왜 그렇게 말합니까?

원칙은 다음과 같습니다: Python의 내장 API를 사용하여 지정된 스레드에서 예외를 트리거하여 자동으로 종료할 수 있도록 합니다.

Python 스레드의 위치 지정 및 소멸에 대한 자세한 소개(예제 포함)

이 방법을 최후의 수단으로 사용하지 마십시오. 형언할 수 없는 문제를 일으킬 확률. 기억하다! 왜 아는지는 묻지 마세요...

스레드를 중지하는 것이 왜 그렇게 어려운가요?

멀티스레딩 자체는 프로세스 하에서 공동 작업을 위해 설계되었습니다. 스레드는 리소스를 공유하는 가장 작은 단위입니다. 프로세스의 메커니즘과 상태 제어가 많습니다.

스레드를 강제로 죽이는 경우 예상치 못한 버그가 발생할 가능성이 높습니다. 그리고 가장 중요한 잠금 리소스 해제로 인해 예상치 못한 문제가 발생할 수도 있습니다.

신호를 통해 프로세스를 종료하는 것처럼 스레드를 직접 종료할 수도 없습니다. 왜냐하면 kill은 프로세스를 처리해야만 우리의 기대치를 달성할 수 있기 때문입니다. 그러나 스레드를 처리하는 것은 분명히 불가능합니다. 출구!

그리고 GIL 때문에 많은 아이들은 Python 스레드가 Python 자체로 구현되어 실제로 존재하지 않는다고 생각합니다. Python을 직접 파괴해야 합니다. 그렇죠?

그러나 사실 Python의 스레드는 실제 스레드입니다!

무슨 뜻인가요? Python 스레드는 운영 체제에서 pthread를 통해 생성된 기본 스레드입니다. Python은 예약을 시작할 시기를 결정하기 위해 이러한 스레드를 제한하는 데에만 GIL을 사용합니다.

간단한 스레드인 경우 시스템에서는 실제로 이를 종료할 수 있는 방법이 있습니다. 예: pthread_exit,pthread_kill  或 pthread_cancel, 자세한 내용은 https://www.cnblogs.com/Creat를 참조하세요...

안타깝습니다. : Python 레벨에는 이러한 메소드 캡슐화가 없습니다! 맙소사, 너무 화가 났어요! 어쩌면 사람들은 실을 부드럽게 다루어야 한다고 생각할 수도 있습니다.

스레드를 부드럽게 종료하는 방법

스레드를 부드럽게 종료하려면 말도 안 되는 소리에 가깝습니다~

실행 후 종료하거나, 플래그 비트를 설정하고, 플래그 비트를 자주 확인하고, 때가 되면 종료하세요. 출구.

Extension

"실행 중인 하위 스레드를 올바르게 종료하는 방법": https://www.cnblogs.com/Creat...
"파이썬 스레드를 대충 파괴하지 마세요": http://xiaorui.cc/2017 /02/22/...

모든 전문가의 조언과 교류를 환영합니다. QQ 토론 그룹: 258498217
재인쇄할 출처를 표시해주세요: https://segmentfault.com/a/11...

  • 모든 권리 보유



제 글이 도움이 되셨다면 공감해주세요 자유롭게 칭찬해주세요

당신이 관심을 가질 수도 있습니다



댓글 2개 ~ 어떻게든 ~ ~                

~ ~                 내가 Kill -9라면 차라리 천 명을 잘못 죽이는 편이 나을 것입니다.

                                                           ~                                                                                                                                                     답글                                   ~ ~ ~ ~ ~ | ~ 9 - -~ -9 이 과정에서 사망~

~ ~                                                                 ~ ~ —                                                                 ~                                         ~ 작가 ~ ~ 답글 추가 入 중간에... 댓글 더 보기

위 내용은 Python 스레드의 위치 지정 및 소멸에 대한 자세한 소개(예제 포함)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 segmentfault.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제

관련 기사

더보기