深度说明Python主线程销毁子线程过程

开发 后端
Python主线程在内核中访问Python解释器之前,需要做的就是先申请GIL,线程在用户级需要访问共享资源之前也需要先申请用户级的lock。

在处理完成后,Python主线程将销毁线程,其实Python主线程的销毁与子线程的销毁是不同的,因为主线程的销毁动作必须要通过销毁Python的运行环境才可以生效,而子线程的销毁则不需要进行这些动作。

Python首先会通过PyThreadState_Clear清理当前线程所对应的线程状态对象。所谓清理,实际上比较简单,就是对线程状态对象中维护的东西进行引用计数的维护。随后,Python释放GIL,释放GIL的操作是在PyThreadState_DeleteCurrent中完成的。

在PyThreadState_DeleteCurrent中,首先会删除当前的线程状态对象,然后通过PyEval_ReleaseLock释放GIL。Python在函数PyThreadState_DeleteCurrent完成了绝大部分线程的销毁动作,剩下的PyThread_exit_thread是一个平台相关的操作,完成各个平台上不同的销毁原生线程的工作。在Win32下,实际上就是调用_endthread。

我们知道,Python的线程在GIL的控制之下,线程之间,对整个Python解释器,对Python提供的C API的访问,都是互斥的,这可以看作是Python内核级的互斥机制。但是这种互斥是我们不能控制的,我们还需要另一种可控的互斥机制——用户级互斥。内核级通过GIL实现的互斥保护了内核的共享资源,同样,用户级互斥保护了用户程序中的共享资源。考虑下面的例子:

  1. [thread2.py]  
  2.  
  3. import thread  
  4.  
  5. import time  
  6.  
  7. input = None 
  8.  
  9. lock = thread.allocate_lock()  
  10.  
  11. def threadProc():  
  12.  
  13.     while True:   
  14.  
  15.         print 'sub thread id : ', thread.get_ident()  
  16.  
  17.         print 'sub thread %d wait lock...' % thread.get_ident()  
  18.  
  19.         lock.acquire()  
  20.  
  21.         print 'sub thread %d get lock...' % thread.get_ident()  
  22.  
  23.         print 'sub thread %d receive input : %s' % (thread.get_ident(), input)  
  24.  
  25.         print 'sub thread %d release lock...' % thread.get_ident()  
  26.  
  27.         lock.release()  
  28.  
  29.         time.sleep(1)  
  30.  
  31. thread.start_new_thread(threadProc, ())  
  32.  
  33. print 'main thread id : ', thread.get_ident()  
  34.  
  35. while True:  
  36.  
  37.     print 'main thread %d wait lock...' % thread.get_ident()  
  38.  
  39.     lock.acquire()  
  40.  
  41.     print 'main thread %d get lock...' % thread.get_ident()  
  42.  
  43.     input = raw_input()  
  44.  
  45.     print 'main thread %d release lock...' % thread.get_ident()  
  46.  
  47.     lock.release()  
  48.  
  49.     time.sleep(1)  

在thread2.py中,有一个Python主线程和子线程之间共享的变量input。这个input是用户的输入,Python主线程接收输入,而子线程打印用户输入。为了保证子线程在用户输入之后才激活打印动作,thread2.py使用了Python线程机制提供的Lock机制来实现同步动作,这实际上也可以视为线程之间的互斥。

主线程通过lock.acquire获得lock之后,将独享对input的访问权利。子线程会因为等待lock而将自身挂起,直到主线程释放lock之后才会被Python的线程调度机制唤醒,获得访问input的权力。注意,这里主线程需要使用sleep使自身挂起,才能触发Python的线程调度,使得子线程获得运行的机会。而这时主线程由于等待lock,同样会将自身挂起,不能再访问input。#t#

于是,自始至终,每一个线程都能控制自己对input的使用,不用担心别的线程破坏input的状态。这种机制给了用户控制线程之间交互的能力,是Python中实现线程互斥和同步的核心。

在本节中,我们将详细剖析Python中Lock机制的实现,有了前面关于Python中线程实现的基础,你会发现,Lock机制的实现真的可以用顺其自然来形容。在进入Lock机制的实现之前,我们先来看看thread2.py的输出结果,以对Lock机制有一个感性的认识。

责任编辑:chenqingxiang 来源: itchinaclub.blog
相关推荐

2010-03-01 13:28:44

Python子线程

2010-02-01 17:33:24

Python主线程

2010-02-02 16:47:12

Python主线程

2024-04-02 09:53:08

线程池线程堆栈

2010-03-01 13:13:02

Python应用线程

2010-02-02 14:42:38

Python线程

2010-02-26 15:37:11

Python主线程

2020-12-21 06:18:15

Android线程主线程

2012-05-14 17:09:05

iOS

2010-03-10 19:34:45

Python主线程

2021-04-08 10:51:10

主线程子线程Thread

2010-02-26 09:42:52

Python线程池

2009-08-12 13:22:44

Singleton模式

2010-03-15 18:34:08

Java多线程

2020-09-07 07:33:01

NodejsCPU密集型

2011-06-29 16:34:11

Qt 子线程 线程

2010-02-03 16:15:05

Python语言

2009-12-08 09:00:14

WCF线程

2017-03-23 18:02:59

Android线程Tread

2011-06-22 15:42:18

QT 信号
点赞
收藏

51CTO技术栈公众号