Python中的进程和线程

您所在的位置:网站首页 线程和进程的关系是什么 Python中的进程和线程

Python中的进程和线程

2023-03-26 20:36| 来源: 网络整理| 查看: 265

线程和进程 1、什么是进程/线程 1.介绍

众所周知,CPU是计算机的核心,承担着所有的计算任务。操作系统是计算机的管理者和大管家。它负责任务调度、资源分配和管理,指挥整个计算机硬件。应用程序是具有一定功能的程序。该程序在操作系统上运行

2.线

早期的计算机没有线程的概念,但随着时代的发展,只用进程来处理程序存在很多不足。例如,当某个进程被阻塞时,整个程序就会停在阻塞的地方,如果频繁切换进程,就会浪费系统资源。所以线程出现了

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

3.过程

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

4.区别 一个进程由一个或多个线程组成。线程是进程中代码的不同执行路径

2.切换进程比切换线程需要更多资源

3、进程相互独立,同一进程中的线程共享程序的内存空间(如代码段、数据集、堆栈等)。进程中的线程对其他进程不可见。换句话说,线程共享相同的内存空间,而进程拥有独立的内存空间

5.利用

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

2、多线程

在 Python 中,线程和进程是通过线程类使用的。这个类在我们的线程和线程模块中。我们通常通过线程导入

默认情况下,如果解释器没有报错,则线程可用

从线程导入线程

1.常用方法

Thread.run(self) # 线程启动时运行的方法,调用target参数指定的函数

Thread.start(self) # 启动一个线程,start方法就是调用run方法

Thread.terminate(self) # 强制线程终止

Thread.join(self, timeout) # 阻塞调用,主线程等待

Thread.setDaemon(self, daemonic) # 设置子线程为守护线程

Thread.getName(self, name) # 获取线程名称

Thread.setName(self, name) # 设置线程名称

2.常用参数

范围

解释

目标

表示调用对象,即子线程要执行的任务

姓名

子线程的名称

参数

传入目标函数的位置参数是一个元组,参数后面必须加逗号

3.多线程的应用 3.1 重写线程方法

导入时间、队列、线程

类 MyThread(threading.Thread):

定义__init__(自我):

超级()。__init__()

self.daemon u003d True # 开启守护模式

self.queue u003d queue.Queue(3) # 打开队列对象,存放三个任务

self.start() # 实例化的时候直接启动线程,不需要手动启动线程

def run(self) -> None: #run方法是线程的方法。它是一个内置方法,会在线程运行时自动调用

while True: # 连续处理任务

函数,参数,kwargs u003d self.queue.get()

func(*args, **kwargs) # 调用函数执行任务的元组长度可变。记得打开包装

self.queue.task_done() # 通过将计数器减一来解决任务以避免阻塞

生产者模型

def submit_tasks(self, func, argsu003d(), kwargsu003d{}): #func是要执行的任务,加上变长参数(默认使用默认参数)

self.queue.put((func, args, kwargs)) # 提交任务

覆盖连接方法

def join(self) -> 无:

self.queue.join() # 检查队列定时器是否为0,如果任务为空,则关闭队列

def f2(*args, **kwargs):

时间.sleep(2)

print("任务 2 完成", args, kwargs)

实例化线程对象

mt u003d MyThread()

提交任务

mt.submit_tasks(f2, argsu003d("aa", "aasd"), kwargsu003d{"a": 2, "s": 3})

让主线程等待子线程完成

mt.join()

守卫模式:

主线程只有在其他非守护线程运行后才运行(此时守护线程被回收)。因为主线程的结束意味着进程的结束,进程的整体资源会被回收,进程必须保证所有非守护线程都运行完才能结束 3.2直接调试

定义 f2(i):

时间.sleep(2)

print("任务 2 完成", i)

利斯 u003d []

对于范围内的 i (5):

t u003d 线程(目标u003df2, argsu003d(i,))

t.start() # 启动5个线程

list.append(t)

for i in lis:

i.join() # 线程等待

4.线程间数据共享

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

这时候可以使用线程库中的Lock对象Lock来保护它

Lock对象的acquire方法是申请一个Lock

在对共享数据对象进行操作之前,每个线程都应该申请获得操作权,即调用共享数据对象对应的锁对象的acquire方法。如果线程A执行了acquire()方法,而其他线程B已经申请了锁并且没有释放,那么线程A的代码会在这里等待线程B释放锁,不要执行下面的代码。

线程A无法获取锁,直到线程B执行锁的释放方法并释放锁,然后可以执行如下代码

例如:

导入线程

是 u003d 1

添加互斥体并获取锁

锁 u003d threading.Lock()

定义两个线程使用的任务

定义函数1():

global var # 声明全局变量

对于我在范围内(1000000):

lock.acquire() # 操作前加锁

其中 +u003d 我

lock.release() # 操作后释放锁

定义函数2():

global var # 声明全局变量

对于我在范围内(1000000):

lock.acquire() # 操作前加锁

其中 -u003d 我

lock.release() # 操作后释放锁

创建2个线程

t1 u003d threading.Thread(targetu003dfunc1)

t2 u003d threading.Thread(targetu003dfunc2)

t1.start()

t2.start()

t1.join()

t2.join()

打印(变量)

使用多线程时,如果数据与你的预期不一致,可以考虑是否调用共享数据并覆盖

使用线程库中的Lock来保护对象

3、多进程使用 1.介绍

Python中的多处理是通过multiprocessing包实现的,多线程Thread也是类似的。它可以利用多处理进程对象来创建进程对象。这个进程对象的方法与线程对象的方法类似。还有start()、run()、join()等方法。一种方法是不同的。线程对象中的daemon方法是setdaemon,而进程对象的daemon是通过设置daemon属性来完成的

2.申请 2.1 改写处理方法

进口时间

从多处理导入过程

class MyProcess(Process): # 继承进程类

def __init__(self, target, argsu003d(), kwargsu003d{}):

超级(MyProcess,自我)。__init__()

self.daemon u003d True # 启动守护进程

self.target u003d 目标

self.args u003d 参数

self.kwargs u003d kwargs

self.start() # 自动启动进程

def 运行(自我):

self.target(*self.args, **self.kwargs)

定义乐趣(*args,**kwargs):

打印(时间。时间())

打印(args[0])

如果 __name__ u003du003d '__main__':

利斯 u003d []

对于范围内的 i (5):

p u003d MyProcess(有趣,argsu003d(1,))

lis.append(p)

for i in lis:

i.join() # 让进程等待

守卫模式:

主进程在其代码完成后运行完毕(此时会回收daemon),然后主进程将等到非daemon子进程运行完毕,回收子进程的资源(否则,将生成一个僵尸进程) 2.2 直接传输使用

进口时间

从多处理导入过程

定义乐趣(*args,**kwargs):

打印(时间。时间())

打印(args[0])

如果 __name__ u003du003d '__main__':

利斯 u003d []

对于范围内的 i (5):

p u003d 过程(目标u003d乐趣,参数u003d(1,))

lis.append(p)

for i in lis:

i.join() # 让进程等待

3.进程间数据共享 3.1 锁定方式

使用方法类似于线程的Lock使用方法

3.2 管理器方法

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

Manager 支持的类型有:list,dict,Namespace,Lock,RLock,Semaphore,BoundedSemaphore,Condition,Event,Queue,Value 和 Array

句法:

from multiprocessing import Process, Lock, Manager

def f(n, d, l, 锁):

lock.acquire()

d[str(n)] u003d n

l[n] u003d -99

lock.release()

如果 __name__ u003du003d '__main__':

锁 u003d 锁()

以 Manager() 作为经理:

d u003d manager.dict() # 空字典

l u003d manager.list(range(10)) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

启动10个进程,不同的进程对d和l中的不同元素进行操作

对于范围内的 i (10):

p u003d 进程(目标u003df, argsu003d(i, d, l, lock))

p.start()

p.join()

打印(d)

打印(升)

4、池并发 1.语法

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

如果使用线程池/进程池来管理并发编程,只要将相应的任务函数提交到线程池/进程池,剩下的就交给线程池/进程池来完成

Executor 提供了以下常用方法:

submit(fn, *args, **kwargs):提交fn函数到线程池*args代表传递给fn函数的参数,*kwargs代表以关键字形式传递给fn函数的参数参数

map(func, *iterables, timeoutu003dNone, chunksizeu003d1):这个函数和全局函数map(func, *iterables)类似,只是这个函数会启动多个线程立即对iterables执行map处理异步。

shutdown(waitu003dTrue):关闭线程池

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

Future 提供了以下方法:

cancel():取消Future代表的线程任务。如果任务正在执行且无法取消,则该方法返回 False;否则,程序取消任务并返回 True。

cancelled():返回Future代表的线程任务是否已经成功取消。

running():如果Future代表的线程任务正在执行且无法取消,则该方法返回True。

done():如果该特征所代表的线程任务成功取消或执行,该方法返回True。

result(timeoutu003dNone):获取Future代表的线程任务最后返回的结果。如果Future代表的线程任务还没有完成,这个方法会阻塞当前线程,timeout参数指定阻塞的最大秒数。

exception(timeoutu003dNone):获取Future代表的线程任务抛出的异常。如果任务成功完成并且没有异常,则该方法返回 None。

add_done_callback(fn):为Future代表的线程任务注册一个“回调函数”。当任务成功完成后,程序会自动触发fn函数

2.获取CPU个数

from multiprocessing import cpu_count #CPU核数模块,可获取CPU核数

n u003d cpu_count() # 获取cpu核心数

3.线程池

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

1.调用ThreadPoolExecutor类的构造函数创建线程池

2.将普通函数定义为线程任务

3.调用ThreadPoolExecutor对象的submit()方法提交线程任务

4、当不想提交任何任务时,调用ThreadPoolExecutor对象的shutdown()方法关闭线程池

从 concurrent.futures 导入 ThreadPoolExecutor

导入线程

进口时间

定义一个准备为线程任务的函数

默认动作(最大):

我_sum u003d 0

对于我在范围内(最大):

打印(threading.current_thread().name + ' ' + str(i))

我_sum +u003d 我

返回我的_sum

创建一个有2个线程的线程池

池 u003d ThreadPoolExecutor(max_workersu003d2)

向线程池提交一个任务,50将作为action()函数的参数

future1 u003d pool.submit(动作,50)

向线程池提交另一个任务,100将作为action()函数的参数

future2 u003d pool.submit(动作,100)

def get_result(未来):

打印(未来。结果())

为future1添加线程完成回调函数

future1.add_done_callback(get_result)

为future2添加线程完成回调函数

future2.add_done_callback(get_result)

判断future1代表的任务是否结束

打印(future1.done())

时间.sleep(3)

判断future2代表的任务是否结束

打印(future2.done())

查看future1代表的任务返回的结果

打印(future1.result())

查看future2代表的任务返回的结果

打印(future2.result())

关闭线程池

pool.shutdown() # 序列可以使用with语句来管理线程池,可以避免手动关闭线程池

最佳线程数 u003d ((线程等待时间 + 线程 CPU 时间) / 线程 CPU 时间) * CPU 数量

它也可以低于 CPU 核心数

3.进程池

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

1.调用ProcessPoolExecutor类的构造函数创建线程池

将正常功能定义为流程任务

3.调用ProcessPoolExecutor对象的submit()方法提交线程任务

4、当不想提交任何任务时,调用ProcessPoolExecutor对象的shutdown()方法关闭线程池

进程的开启代码必须放在 if__ name__ u003du003d '__ main__': 代码下,不能放在函数或其他地方

启动过程的提示

从 concurrent.futures 导入 ProcessPoolExecutor

pool u003d ProcessPoolExecutor(max_workersu003dcpu_count()) #根据cpu核心数启动多少进程

打开的进程数应低于最大 CPU 核数



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3