Python:进程(threading)

Python013

Python:进程(threading),第1张

这里是自己写下关于 Python 跟进程相关的 threading 模块的一点笔记,跟有些跟 Linux 调用挺像的,有共通之处。

https://docs.python.org/3/library/threading.html?highlight=threading#thread-objects

直接传入

继承 Thread 重写 run 方法

threading.Thread(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)

group 线程组,未实现

start() 线程就绪

join([timeout]) 阻塞其他线程,直到调用这方法的进程结束或时间到达

RuntimeError: cannot join thread before it is started

get/setName(name) 获取/设置线程名。

isAlive() 返回线程是否在运行。

is/setDaemon(bool): 获取/设置是后台线程(默认前台线程(False))。(在start之前设置)

The entire Python program exits when no alive non-daemon threads are left.

没有非后台进程运行,Python 就退出。

主线程执行完毕后,后台线程不管是成功与否,主线程均停止

t.start()

t.join()

start() 后 join() 会顺序执行,失去线程意义

https://docs.python.org/3/library/threading.html?#lock-objects

Lock属于全局,Rlock属于线程(R的意思是可重入,线程用Lock的话会死锁,来看例子)

acquire(blocking=True, timeout=-1) 申请锁,返回申请的结果

release() 释放锁,没返回结果

https://docs.python.org/3/library/threading.html#condition-objects

可以在构造时传入rlock lock实例,不然自己生成一个。

acquire([timeout])/release(): 与lock rlock 相同

wait([timeout]): 调用这个方法将使线程进入等待池,并释放锁。调用方法前线程必须已获得锁定,否则将抛出异常。

notify(): 调用这个方法将从等待池挑选一个线程并通知,收到通知的线程将自动调用acquire()尝试获得锁定(进入锁定池);其他线程仍然在等待池中。调用这个方法不会释放锁定。调用方法前线程必须已获得锁定,否则将抛出异常。

notifyAll(): 调用这个方法将通知等待池中所有的线程,这些线程都将进入锁定池尝试获得锁定。调用这个方法不会释放锁定。使用前线程必须已获得锁定,否则将抛出异常。

threading.Semaphore(value=1)

https://docs.python.org/3/library/threading.html#semaphore-objects

acquire(blocking=True, timeout=None)

资源数大于0,减一并返回,等于0时等待,blocking为False不阻塞进程

返回值是申请结果

release()

资源数加1

https://docs.python.org/3/library/threading.html#event-objects

事件内置了一个初始为False的标志

is_set() 返回内置标志的状态

set() 设为True

clear() 设为False

wait(timeout=None) 阻塞线程并等待,为真时返回。返回值只会在等待超时时为False,其他情况为True

https://docs.python.org/3/library/threading.html#timer-objects

threading.Timer(interval, function, args=None, kwargs=None)

第一个参数是时间间隔,单位是秒,整数或者浮点数,负数不会报错直接执行不等待

可以用cancel() 取消

https://docs.python.org/3/library/threading.html#barrier-objects

threading.Barrier(parties, action=None, timeout=None)

调用的进程数目达到第一个设置的参数就唤醒全部进程

wait(timeout=None)

reset() 重置,等待中的进程收到 BrokenBarrierError 错误

目录

众所周知,CPU是计算机的核心,它承担了所有的计算任务。而操作系统是计算机的管理者,是一个大管家,它负责任务的调度,资源的分配和管理,统领整个计算机硬件。应用程序是具有某种功能的程序,程序运行与操作系统之上

在很早的时候计算机并没有线程这个概念,但是随着时代的发展,只用进程来处理程序出现很多的不足。如当一个进程堵塞时,整个程序会停止在堵塞处,并且如果频繁的切换进程,会浪费系统资源。所以线程出现了

线程是能拥有资源和独立运行的最小单位,也是程序执行的最小单位。一个进程可以拥有多个线程,而且属于同一个进程的多个线程间会共享该进行的资源

① 200 多本 Python 电子书(和经典的书籍)应该有

② Python标准库资料(最全中文版)

③ 项目源码(四五十个有趣且可靠的练手项目及源码)

④ Python基础入门、爬虫、网络开发、大数据分析方面的视频(适合小白学习)

⑤ Python学习路线图(告别不入流的学习)

私信我01即可获取大量Python学习资源

进程时一个具有一定功能的程序在一个数据集上的一次动态执行过程。进程由程序,数据集合和进程控制块三部分组成。程序用于描述进程要完成的功能,是控制进程执行的指令集;数据集合是程序在执行时需要的数据和工作区;程序控制块(PCB)包含程序的描述信息和控制信息,是进程存在的唯一标志

在Python中,通过两个标准库 thread和Threading提供对线程的支持,threading对thread进行了封装。threading模块中提供了Thread,Lock,RLOCK,Condition等组件

在Python中线程和进程的使用就是通过 Thread这个类。这个类在我们的thread和threading模块中。我们一般通过threading导入

默认情况下,只要在解释器中,如果没有报错,则说明线程可用

守护模式:

现在我们程序代码中,有多个线程, 并且在这个几个线程中都会去 操作同一部分内容,那么如何实现这些数据的共享呢?

这时,可以使用 threading库里面的锁对象 Lock 去保护

Lock 对象的acquire方法 是申请锁

每个线程在操作共享数据对象之前,都应该申请获取操作权,也就是调用该共享数据对象对应的锁对象的acquire方法,如果线程A 执行了 acquire()方法,别的线程B 已经申请到了这个锁, 并且还没有释放,那么 线程A的代码就在此处 等待 线程B 释放锁,不去执行后面的代码。

直到线程B 执行了锁的 release 方法释放了这个锁, 线程A 才可以获取这个锁,就可以执行下面的代码了

如:

到在使用多线程时,如果数据出现和自己预期不符的问题,就可以考虑是否是共享的数据被调用覆盖的问题

使用 threading库里面的锁对象Lock去保护

Python中的多进程是通过multiprocessing包来实现的,和多线程的threading.Thread差不多,它可以利用multiprocessing.Process对象来创建一个进程对象。这个进程对象的方法和线程对象的方法差不多也有start(), run(), join()等方法,其中有一个方法不同Thread线程对象中的守护线程方法是setDeamon,而Process进程对象的守护进程是通过设置daemon属性来完成的

守护模式:

其使用方法和线程的那个 Lock 使用方法类似

Manager的作用是提供多进程共享的全局变量,Manager()方法会返回一个对象,该对象控制着一个服务进程,该进程中保存的对象运行其他进程使用代理进行操作

语法:

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

如果使用线程池/进程池来管理并发编程,那么只要将相应的 task 函数提交给线程池/进程池,剩下的事情就由线程池/进程池来搞定

Exectuor 提供了如下常用方法:

程序将 task 函数提交(submit)给线程池后,submit 方法会返回一个 Future 对象,Future 类主要用于获取线程任务函数的返回值。由于线程任务会在新线程中以异步方式执行,因此,线程执行的函数相当于一个“将来完成”的任务,所以 Python 使用 Future 来代表

Future 提供了如下方法:

使用线程池来执行线程任务的步骤如下:

最佳线程数目 = ((线程等待时间+线程CPU时间)/线程CPU时间 )* CPU数目

也可以低于 CPU 核心数

使用线程池来执行线程任务的步骤如下:

关于进程的开启代码一定要放在 if __name__ == '__main__':代码之下,不能放到函数中或其他地方

开启进程的技巧

开启进程的数量最好低于最大 CPU 核心数