Python中的进程和线程 |
您所在的位置:网站首页 › 线程和进程的关系是什么 › Python中的进程和线程 |
线程和进程
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 |