python 12

一、线程
本节就以 threading 模块为例进行讲解。Python 主要通过两种方式来创建线程:
1.使用 threading 模块中 Thread 类的构造器创建线程。即直接对类 threading.Thread 进行实例化,并调用实例化对象的 start 方法创建线程。

2。继承 threading 模块中的 Thread 类创建线程类。即用 threading.Thread 派生出一个新的子类,将新建类实例化,并调用其 start 方法创建线程。
在这里插入图片描述
在这里插入图片描述
虽然上面程序只显式创建并启动了两个线程,但实际上程序有三个线程,即程序显式创建的两个子线程和主线程。前面己经提到,当 Python 程序开始运行后,程序至少会创建一个主线程,主线程的线程执行体就是程序中的主程序(没有放在任何函数中的代码)。
在这里插入图片描述

继承 Thread 类创建线程类

继承 Thread 类创建线程类

二、Python线程的生命周期(新建、就绪、运行、阻塞和死亡)

在这里插入图片描述
在这里插入图片描述

线程的运行和阻塞状态

在这里插入图片描述

线程死亡

1.线程会以如下方式结束,结束后就处于死亡状态:
run() 方法或代表线程执行体的 target 函数执行完成,线程正常结束。

2.线程抛出一个未捕获的 Exception 或 Error。
注意,当主线程结束时,其他线程不受任何影响,并不会随之结束。一旦子线程启动起来后,它就拥有和主线程相同的地位,不会受主线程的影响。

为了测试某个线程是否己经死亡,可以调用线程对象的 is_alive() 方法,当线程处于就绪、运行、阻塞三种状态时,该方法将返回 True;当线程处于新建、死亡两种状态时,该方法将返回 False

不要试图对一个已经死亡的线程调用 start() 方法使它重新启动,死亡就是死亡,该线程将不可再次作为线程运行。下面程序尝试对处于死亡状态的线程再次调用 start() 方法:

三、Thread join()用法详解

Thread 提供了让一个线程等待另一个线程完成的 join() 方法。当在某个程序执行流中调用其他线程的 join() 方法时,调用线程将被阻塞,直到被 join() 方法加入的 join 线程执行完成。

在这里插入图片描述

join(timeout=None)方法可以指定一个 timeout 参数,该参数指定等待被 join 的线程的时间最长为 timeout 秒。
如果在 timeout 秒内被 join 的线程还没有执行结束,则不再等待。

四、守护线程及作用(包含2种创建方式)

1.有一种线程,它是在后台运行的,它的任务是为其他线程提供服务,这种线程被称为**“后台线程(Daemon Thread)”,又称为“守护线程”或“精灵线程”**。Python 解释器的垃圾回收线程就是典型的后台线程。

2.调用 Thread 对象的 daemon 属性可以将指定线程设置成后台线程。下面程序将指定线程设置成后台线程,可以看到当所有的前台线程都死亡后,后台线程随之死亡。当在整个虚拟机中只剩下后台线程时,程序就没有继续运行的必要了,所以程序也就退出了。
在这里插入图片描述

五、sleep()函数用法:线程睡眠

在这里插入图片描述

六、互斥锁同步线程

在这里插入图片描述在这里插入图片描述

Lock 是控制多个线程对共享资源进行访问的工具。通常,锁提供了对共享资源的独占访问,每次只能有一个线程对 Lock 对象加锁,线程在开始访问共享资源之前应先请求获得 Lock 对象。当对共享资源访问完成后,程序释放对 Lock 对象的锁定。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

七、什么是死锁,如何避免死锁(4种方法)

在这里插入图片描述

八、 condition实现线程通信(详解版)
当线程在系统中运行时,线程的调度具有一定的透明性,通常程序无法准确控制线程的轮换执行,如果有需要,Python 可通过线程通信来保证线程协调运行。
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

八、Queue队列实现线程通信

在这里插入图片描述
在掌握了 Queue 阻塞队列的特性之后,在下面程序中就可以利用 Queue 来实现线程通信了。
在这里插入图片描述
九、Event实现线程通信

在这里插入图片描述

在这里插入图片描述

运行上面程序,将看到如下输出结果:

Thread-1 启动
甲 准备开始计算状态
Thread-2 启动
乙 准备开始计算状态
------------------
主线程发出事件
Thread-1 收到通知了.
Thread-2 收到通知了.
甲 正式开始计算!
乙 正式开始计算!

上面程序还没有使用 Event 的内部旗标,如果结合 Event 的内部旗标,同样可实现前面的 Account 的生产者-消费者效果:存钱线程(生产者)存钱之后,必须等取钱线程(消费者)取钱之后才能继续向下执行。
Event 实际上优点类似于 Condition 和旗标的结合体,但 Event 本身并不带 Lock 对象,因此如果要实现线程同步,还需要额外的 Lock 对象。

在这里插入图片描述
在这里插入图片描述
1.所有的设置旗帜后,建议延时以待完全。
2.实际操作过程中,set会在设置时唤醒draw进程,建议后面进行时间停顿,等待draw函数clear 旗帜。
在这里插入图片描述
3.如果旗帜无论是否为True都需等待的话,可以得到限定次数的,比如说各个操作有效的10次,但是由于最后达到i,j为9时,wait使得线程阻塞,程序无法停下。

如果对旗帜TRUE进行判断,将进行有效操作一半的次数,但可以结束。

十、线程池及其原理和使用

线程池的基类是 concurrent.futures 模块中的 Executor,Executor 提供了两个子类,即 ThreadPoolExecutor 和 ProcessPoolExecutor,其中 ThreadPoolExecutor 用于创建线程池,而 ProcessPoolExecutor 用于创建进程池。

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
程序最后通过 Future 的 result() 方法来获取两个异步任务返回的结果。
当程序使用 Future 的 result() 方法来获取结果时,该方法会阻塞当前线程,如果没有指定 timeout 参数,当前线程将一直处于阻塞状态,直到 Future 代表的任务返回。

获取执行结果

前面程序调用了 Future 的 result() 方法来获取线程任务的运回值,但该方法会阻塞当前主线程,只有等到钱程任务完成后,result() 方法的阻塞才会被解除。
如果程序不希望直接调用 result() 方法阻塞线程,则可通过 Future 的 add_done_callback() 方法来添加回调函数,该回调函数形如 fn(future)。当线程任务完成后,程序会自动触发该回调函数,并将对应的 Future 对象作为参数传给该回调函数。
在这里插入图片描述
1.由于程序并未直接调用 future1、future2 的 result() 方法,因此主线程不会被阻塞,可以立即看到输出主线程打印出的横线。接下来将会看到两个新线程并发执行,当线程任务执行完成后,get_result() 函数被触发,输出线程任务的返回值。

2.另外,由于线程池实现了上下文管理协议(Context Manage Protocol),因此,程序可以使用 with 语句来管理线程池,这样即可避免手动关闭线程池,
在这里插入图片描述

十一、threading Local()函数用法:返回线程局部变量

Python 在 threading 模块下提供了一个 local() 函数,该函数可以返回一个线程局部变量,通过使用线程局部变量可以很简捷地隔离多线程访问的竞争资源,从而简化多线程井发访问的编程处理。

线程局部变量(Thread Local Variable)的功用其实非常简单,就是为每一个使用该变量的线程都提供一个变量的副本,使每一个线程都可以独立地改变自己的副本,而不会和其他线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有该变量一样。
在这里插入图片描述

十二、 Timer定时器:控制函数在特定时间执行

在这里插入图片描述

十三、schedule任务调度及其用法

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

十三、os.fork()方法:创建新进程

在这里插入图片描述

十四、Process创建进程(2种方法)详解

在这里插入图片描述
在这里插入图片描述
需要说明的是,通过 multiprocessing.Process 来创建并启动进程时,程序必须先判断if name==‘main’:,否则可能引发异常。

if __name__ == "__main__"语句

在Windows操作系统中由于没有fork(linux操作系统中创建进程的机制),在创建子进程的时候会自动 import 启动它的这个文件,而在 import 的时候又执行了整个文件。因此如果将process()直接写在文件中就会无限递归创建子进程报错。所以必须把创建子进程的部分使用if name ==‘main’ 判断保护起来,import 的时候就不会递归运行了。

继承Process类创建子进程
继承 Process 类创建子进程的步骤如下:
1.定义继承 Process 的子类,重写其 run() 方法准备作为进程执行体。
2.创建 Process 子类的实例。
3.调用 Process 子类的实例的 start() 方法来启动进程。

在这里插入图片描述
在这里插入图片描述
十五、设置进程启动的3种方式

在这里插入图片描述
1.multiprocessing 模块提供了一个set_start_method() 函数,该函数可用于设置启动进程的方式

  multiprocessing.set_start_method('spawn')

2.get_context() 方法来获取 Context 对象,调用该方法时可传入 spawn、fork 或 forkserver 字符串。

 ctx = multiprocessing.get_context('fork')

在这里插入图片描述

十六、多进程编程和多线程编程优缺点

多进程的优点是稳定性好,一个子进程崩溃了,不会影响主进程以及其余进程。基于这个特性,常常会用多进程来实现守护服务器的功能。

多进程编程也有不足,即创建进程的代价非常大,因为操作系统要给每个进程分配固定的资源,并且操作系统对进程的总数会有一定的限制,若进程过多,操作系统调度都会存在问题,会造成假死状态。

多线程编程的优点是效率较高一些,适用于批处理任务等功能;不足之处在于,任何一个线程崩溃都可能造成整个进程的崩溃,因为它们共享了进程的内存资源池。

十七、Python使用进程池管理进程

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

十八、进程间通信的2种实现方法(Queue和Pipe)

Python 为进程通信提供了两种机制:
1.Queue:一个进程向 Queue 中放入数据,另一个进程从 Queue 中读取数据。
在这里插入图片描述
2.Pipe:Pipe 代表连接两个进程的管道。程序在调用 Pipe() 函数时会产生两个连接端,分别交给通信的两个进程,接下来进程既可从该连接端读取数据,也可向该连接端写入数据。
使用 Pipe 实现进程通信,程序会调用 multiprocessing.Pipe() 函数来创建一个管道,该函数会返回两个 PipeConnection 对象,代表管道的两个连接端(一个管道有两个连接端,分别用于连接通信的两个进程)。
在这里插入图片描述
在这里插入图片描述

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值