python编程金典读书笔记_《Python编程金典》读书笔记下(ZZ)

单线程程序问题在于要在结束费时较长的操作后,才能开始其它操作。而在多线程程序中,线程可共享一个或多个处理器,使多个任务并行执行。

释器开始执行程序时,“主”线程开始执行。每个线程都可创建和启动其它线程。如果程序包含多个正在运行的线程,它们将依据指定的间隔时间(称为一个

quantum),依次进入和离开解释器。Python的“全局解释器锁”(Global Interpreter

Lock,GIL)保证解释器在任何时刻只运行一个线程。GIL每次可用时,都会有单个线程包含它,然后,线程进入解释器,关在该线程的quantum时

间段中执行它。一旦quantum到期,线程就离开解释器,同时释放GIL。

在任何时刻,线程都处于某种线程状态。新线

程将从“born”状态开始它的生命周期。线程保持这个状态,直到程序调用线程的start方法,这会使线程进入“ready”(就绪)状态,有时也称为

“runnable”状态。另外,控制权会立即返回至调用线程(调用者)。之后,调用者可与已启动的线程以及程序中的其他任何线程并发执行。当

“ready”线程首次获得GIL(Global Interpreter

Lock,全局解释器锁),会执行它的run方法,成为一人“running”(正在运行)线程。run方法会一直执行,直到线程引发一个未处理的异常,

或者线程离开解释器。running线程离开解释器时,线程会记住它的当前执行位置。以后线程重新进入解释器,线程会从该位置继续执行。线程惟一能获得

GIL的状态就是“running”状态。

run方法返回或终止(如遇到一个未进行捕捉的异常),就会进入“dead”状态。解释器最终会对dead线程进行处理。如果running线程调用另一个线程的join方法,running线程会失去GIL,并等待加入的方法死亡之后才会继续。

多数程序都使用外部资源(比如网络连接和磁盘文件)来执行任务。如果线程请求的资源不可用,线程就会进入“blocked”(暂停或阻塞)状态,直到资源

再次可用,线程发了I/O请求后(比如从磁盘上读入文件,或将文件发送到打印机),会失去GIL,并离开解释器。其它线程就中使用解释器,从而可高效利用

处理器,有助缩短程序的总体执行时间。I/O操作完成后,在“blocked”状态等候它的线程进入“ready”状态,之后,线程就会试图重新获得

GIL。

“running”线程调用time.sleep函数后,会释放GIL并进入“sleeping”(休眠)状态。指定的休眠时间到期,“sleeping”线程会返回“ready”状态。即使解释器可用,“sleeping”线程也不能使用解释器。

程序中的线程通常共享数据。如果多个线程修改相同的数据,数据会出错,在这样的程序中,需要同步对共享数据的访问。这意味着访问共享数据的每个线程首先必须获得与数据对应的一个同步对象锁。一旦线程处理完数据,就应释放同步对象,使其它线程能访问数据。

时因为一个程序的逻辑需求,正在运行的线程即使为共享数据获得了同步对象,也不能对其执行操作。这种情况下,线程可调用同步对象的wait方法以主动释放

对象。这会导致线程释放GIL并针对那个同步对象进入“waiting”状态。另一个线程调用同步对象的notify方法时,那个同步对象的一个

“waiting”线程会变成“ready”状态。在重新获得GIL后,线程就会恢复执行。“running”线程调用同步对象的notifyAll方

法,处于“waiting”状态的每个线程都会变成“ready”状态。然后,解释器选择一个“ready”线程来执行。

threading.currentThread函数会返回对当前正在运行的线程的一个引用。

threading.enumerate函数返回一个列表,其中包含Thread类的当前所有活动对象(即run方法开始但未终止的任何线程)。

threading.activeCount函数返回上面列表的长度。

isAlive可测试线程是否死亡,如果返回1代表线程正在运行;setName方法设置线程名称;getName方法返回线程名称;为一个线程使用print方法会显示线程名称及其当前状态。

通常将访问共享数据的代码区称为“临界区”

访

问共享数据的每个线程都禁止其他所有线程同时访问相同的数据。这称为“独占”或“线程同步”。threading模块提供了许多线程同步机制。最简单的同

步机制是“锁”。锁对象用threading.RLock类创建,它定义了两个方法,即acquire(获得)和release(释放)。线程调用

acquire方法,锁会进入“locked”(锁定)状态,每次只有一个线程可获得锁。如另一个线程试图对同一个锁对象调用acquire方法,操作系

统会将那个线程转变为“blocked”状态,直到锁变得可用。拥有锁的线程调用release方法,锁会进入“unlocked”(解锁)状态。

“blocked”的线程会收到一个通知,并可获得锁。如果多个锁处于“blocked”状态,所有线程都会先解除该状态。然后,操作系统选择一个线程来

获得锁,再将其余线程变回“blocked”状态。

在多线程程序中,线程必须先获得一个锁,再进入临界区;退出临界区时,则要释放锁。

复杂的线程中,有时只在发生某些事件时才访问一个临界区(比如在某个数据值改变时)。这是通过“条件变量”来完成的。线程用条件变量来监视一个对象的状

态,或者发出事件通知。对象状态改变或事件发生时,处于blocked状态的线程会收到通知。收到通知后线程才访问临界区。

件变量用threading.Condition类创建。条件变量包含基本锁,所以它们提供了acquire和release方法。条件变量的其它方法还

有wait和nofify。线程成功获得一个基本锁后,调用wait方法会导致调用线程释放这个锁,并进入“blocked”状态,直到另一个线程调用同

一个条件变量的notify方法,从而将其唤醒。notify方法可唤醒一个正在等待条件变量的线程;notifyAll则唤醒所有正在等待的方法。

面通过一个“生产者/消费者”的关系,说明线程同步的应用情况。在这个关系中,应用程序的“生产者”部分生成数据,“消费者”部分使用数据,在多线程的生

产者/消费者关系中,“生产者线程”调用一个“生产方法”来生成数据,并将数据放到名为“缓冲区”的共享内存区域。“消费者线程”则调用一个“消费方法”

来读取数据。如果正在等待放入下一批数据的生产者线程发现消费者线程尚未从缓冲区中读取上一批数据,生产者线程就会调用条件变量的wait方法;否则,消

费者线程将无法看到上一批数据。消费者线程读取数据时,应调用条件变量的notify方法,使正在等待的生产者线程继续。如果消费者线程发现缓冲区为空,

或上一批数据已读取,就应调用条件变量的wait方法;否则,消费者线程会从缓冲区读入“垃圾”数据,或者重复处理以前的数据项--任何一种可能都会导致

应用程序出现逻辑错误。生产者将下一批数据放入缓冲区时,生产者线程应调用条件变量的notify方法,让消费者线程继续。

为尽可能缩短共享资源并以相同相对速度工作的各线程的等待时间,可用一个“队列(Queue)”来提供额外的缓冲区,便于生产者在其中放值,也便于消费者从中获得那些值。

python提供一个Queue模块,该模块定义一人Queue类,即队列的一个同步实现。

“信号机”(Semaphore)是一个变量,控制着对公共资源或临界区的访问。信号机维护着一个计数器,指定可同时使用资源或进入临界区的线程数。每次有一个线程获得信号机时,计数器都会自减。若计数器为0,其它线程便只能暂停访问信号机,直到另一个线程释放信号机。

threading模块定义了可用于线程通信的Event(事件)类。Event有一个内部标记,可为true或false。一个或多个线程能调用Event对象的wait方法以暂停并等待事件发生。事件发生后,暂停的线程会按它们抵达的顺序被唤醒,并恢复执行。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值