waitpid
2. 理解什么是僵尸进程什么是孤儿进程即两者产生的过程 3. 知道僵尸进程的危害和两种处理方法 4. 理解进程的创建流程 创建二级子进程处理 在父进程中使用信号处理的方法忽略子进程发来的信号 signal(SIGCHLD,SIG_IGN) 更方便高效的进程创建方法 multiprocessing模块 (标准库模块) 创建的进程的步骤 1. 将要完成的事件封装成一个个函数 2. 使用multiprocessing提供的接口函数创建进程 3. 使新的进程和指定的函数相关联去完成函数中的工作 4. 对进程进行回收处理 * 函数当付给Process 的target变量后函数内容就是对应进程的进程内容,此时函数才有特殊性 * 多个子进程和父进程之间的执行相互不影响 创建子进程 Process() 类 参数: target 指定要绑定的函数 name 给创建的进程起一个名字 args 需要一个元组,给target指定的函数按位置传参 kwargs 需要给一个字典,给target指定的函数按键值 传参 Process() 类 ----- 》 p 进程对象 属性方法 print("进程名称:",p.name) print("进程PID:",p.pid) print(\'进程状态:\',p.is_alive()) 启动子进程 start() * start() 时才真正的创建子进程,而不是Process时创建 回收子进程 join([timeout]) timeout : 设置最长阻塞时间,如果超过这个时间还没有子进程退出则不再继续等待 * 内核会帮助应用层记录子进程的退出情况,当使用join函数时内核会及时返回进程状态给应用层进行处理 p.daemon 默认值为False 表示主进程运行结束后 不会影响子进程的运行,知道子进程运行完,进程才会结束 如果设置为True 则主进程运行完毕则所有子进程也不再运行一起退出 * 该属性的设置必须要在start()前 * 该属性的设置并不是 将进程设置为 linux/unix中的守护进程 守护进程: 生命周期长, 与前端控制台无关,后台运行,一般用作系统进程或者自动化运行进程 多进程编程 优点: 可以并行的执行多个任务,提高运行效率 空间独立,数据安全 创建方便 缺点:进程的创建和销毁过程需要消耗较多的计算机资源 在需要频繁的创建和删除较多进程的情况下,资源消耗过多,不适宜使用多进程完成任务 进程池技术 1. 创建进程池 ,在池内放入合适数量的进程 2. 将事件加入进程池的等待队列 3. 使用进程池内的进程不断的执行等待事件 4. 所有事件处理结束后关闭回收进程池 Pool 功能:创建进程池 参数: processes :进程池中进程的数量 apply_async() 功能: 以异步的方式将要执行的事件放入进程池 参数: func : 要执行的函数 args : 给函数按位置传参 kwds : 给函数按照键值传参 返回值 : 返回事件执行后的返回值对象,可以通过调用get() 函数获取事件函数return的内容 apply() 功能 : 按照顺序添加要执行的时间,执行一个再添加一个 close() 功能 : 关闭进程池,使其不能再加入新的事件 join() 功能:阻塞等待进程池将时间都执行结束后回收进程池 map() 功能 : 类似与内建函数map 将第二个参数的迭代对象中的数据逐个带入第一个函数作为参数。只不过兼顾了apply_async功能 pool.map(fun,test) ====> for i in test: pool.apply_async(fun,(i,)) 创建自己的进程类 1. 继承Process类以获取原有的属性 2. 实现自己需要的功能部分 3. 使用自己的类创建进程即可 进程间通信 管道 消息队列 共享内存 信号 套接字 管道 在内存中开辟一个管道空间,对多个进程可见。在通信形式上形成一种约束。 linux 文件类型 b c d - l s p 目录 普通文件 链接 套接字 管道 multiprocessing ---》 Pipe 函数 Pipe(duplex) 功能 : 创建一个管道 参数 : duplex 默认为 True 表示管道为双向管道 如果设置为False 则表示管道为单向管道 返回值 : 返回两个管道流对象,分别表示管道的两端 如果参数为True(默认) 两个对象均可发送接受 如果为False时 则第一个对象只能接受,第二个对象只能发送 * 向管道发送数据使用send()函数,从管道接受数据使用recv()函数 * recv()函数为阻塞函数,当管道中数据为空的时候会阻塞 * 一次recv() 只能接受一次send()的内容 * send()可以发送字符串数字列表等多种类型数据 消息队列 multiprocessing --- 》 Queue 在内存中开辟一个队列模型,用来存放消息。任何拥有队列对象的进程都可以进行消息的存放和取出 Queue(maxsize = 0) 功能 : 创建一个消息队列对象 参数 : maxsize 默认为0 表示消息队列可以存放的消息有 系统自动分配的空间而定 > 0 正整数 表示队列中最多存放多少条消息 返回值 : 消息队列对象 q.put() 向消息队列中存放一条消息,当消息队列满的时候,会阻塞 存放的消息类型可以使数字列表,字串等 q.full() 判断队列是否为满,如果满则返回True 否则返回False q.qsize() 查看当前队列中消息数量 q.get() 获取消息,每次获取一条,当消息队列为空是,则阻塞 q.empty() 消息队列为空则返回True 不为空返回False * put get 中block参数和timeout参数 block 默认为True 表示两个函数都是阻塞函数 如果设置为False则表示不阻塞 timeout 当block设置为True的时候表示超时等待时间 共享内存 在内存中开辟一段内存空间存储数据,每次存储的内容会覆盖上次的内容。由于没有对内存进行格式化的修饰所以存取速度块效率高 from multiprocessing import Value,Array obj = Value(ctype,obj) 功能 : 开辟共享内存 参数 : ctype 要转变的c的类型 obj 要写入共享内存的初始值 obj.value 属性为获取共享内存中的值 obj = Array(ctype,obj) 功能 : 开辟一个共享内存空间 参数 : 要转换的c的类型 obj : 放入共享内存中的数据,是一个列表,要求列 表中的数据为相同类型数据 如果obj传入一个正数,则表示在共享内存中开辟一个 多大的空间,空间中可以存放的数值类型 信号 : kill -l 查看系统信号 kill -signame PID 给进程号PID的进程发送signame信号 信号 : 信号名称 含义 默认处理方法 名称 : 系统定义 含义 : 系统定义 处理方式 : 采用默认方式处理 (系统定义 终止 暂停 忽略 忽略信号(当信号没发生过) 采用自定义的方式处理 如何发送信号: os.kill(pid,sig) 功能 : 向一个进程发送一个信号 参数 : pid :要发送进程的PID号 sig :要发送的信号 signal.alarm(sec) 功能:给自己发送一个时钟信号 (SIGALRM) 参数: sec : 秒数 表示在相应的秒数后发送时钟信号 * 信号是一种异步的进程间通信方式 * alarm 函数在一个进程中如果使用多次,则后面的时钟时间会覆盖前面的时间 信号的处理: signal.pause() 阻塞等待一个信号的发生 signal.signal(signum,handler) 功能 : 处理信号 参数 : signum : 表示可以处理的信号 handler : 信号的处理方法 默认处理方式 : SIG_DFL 忽略信号 : SIG_IGN 自定义的方式 : function * signal函数也是一个异步处理信号函数 * SIGSTOP 和 SIGKILL不能被signal函数处理 僵尸进程的信号处理方案 父进程中 signal(SIGCHLD,SIG_IGN) 同步和互斥 临界资源 : 对多个进程或者线程都可见的资源,容易产生争夺,我们将这类资源称为临界资源 临界区 : 对临界资源进行操作的代码区域称之为临界区 解决资源争夺: 同步 或者 互斥 同步 : 同步是一种合作关系,为完成某种任务而建立的多个进程或者线程之间的协调调用,次序等待,传递消息告知资源占用情况 互斥 : 互斥是一种制约关系,当一个进程或者线程进入到临界区后会进行枷锁操作,此时其他进程(线程)无法进如临界区,只有当该 进程(线程)使用后进行解锁,其他人才可以使用。这种技术往往是通过阻塞完成 进程间通信 管道 Pipe recv send 队列 Queue qsize full empty put get 共享内存 Value Array 信号 kill pause alarm signal 同步和互斥 Event() ----> 事件对象e e.wait() : 产生一种阻塞 ,知道e被set之后才结束阻塞 e.set() : 将e set操作 wait不再阻塞 e.is_set() : 判断e是否是被设置的状态 e.clear() : 将e 变成 没有被设置的状态 进程间同步互斥方法 from multiprocessing import Lock 创建 进程锁对象 lock = Lock() lock.acquire() 给临界区上锁 lock.release() 给临界区解锁 * 具体实现上 acquire() 为一个条件阻塞函数 当有任意一个进程先进行了acquire操作后,其他进程再企图进行acquire操作时就会阻塞, 直到lock对象被 release 后其他进程才可进行下次acquire操作 with lock: 也可以实现加锁 解锁 线程 * 线程也可以使用计算机的多核资源,也是多任务编程方式之一 * 线程又称为轻量级的进程,在并发上和进程相同但是在创建时消耗资源少 一个进程中可以包含多个线程,这多个线程共享进程的资源 多个线程因为共享进程的资源所以在通信上往往采用全局变量的方法 线程也有自己特有的资源,比如 TID 指令集等 多进程和多线程的区别和联系 1. 多进程和多线程都是多任务编程方式,都可以使用计算机多核 2. 进程的创建要比线程消耗更多的资源 3. 进程空间独立数据更安全,有专门的进程间通信方式进行交互 4. 一个进程包含多个线程,所以线程共享进程资源,没有转门的通信方法,依赖全局量进行通信。往往需要使用同步互斥机制,逻辑需 要考虑更多 5. 进程线程都有自己特有的资源。多个关联任务的时候使用多线程资源消耗更少,如果是多个无关任务也不适于全都使用线程 创建线程 import threading 创建线程函数 threading.Tread() 功能 : 创建线程 参数 : target 线程函数 args 以元组方式给线程函数传参 kwargs 以字典方式给线程函数传参 name 线程名称 (默认Thread-1) 返回值 : 返回线程对象 线程属性和方法 t.start() 启动一个线程 t.is_alive() 查看一个线程的状态 t.name 查看线程的名称 t.join([sec]) 阻塞等待回收线程 daemon 属性 设置 该属性默认为False 主线程执行完毕不会影响其他线程的执行 如果设置为True 则主线程执行完毕其他线程也终止执行 t.setDaemon(True) 或 t.daemon = True 获取daemon属性值 t.isDaemon() 线程间的通信 全局变量进行通信 线程间的同步和互斥 线程 event 创建事件对象 e = threading.Event() e.wait([timeout]) 如果e被设置则不会阻塞,未被设置则阻塞 timeout为阻塞的超时时间 e.set() 将e变为设置的状态 e.clear() 将e变为未设置的状态 线程锁 lock = threading.Lock() 创建锁对象 lock.acquire() 上锁 lock.release() 解锁 创建自己的线程类 1. 自定义类 继承于 原有线程类 Thread 2. 复写原有的run方法 3. 创建线程对象调用start 的时候会自动执行run threadpool 线程池第三方模块 sudo pip3 install threadpool GIL (全局解释器锁) python -- 》 支持多线程 ----》 同步和互斥 ---》加锁 ---》超级锁 ----》 解释器在同一时刻只能解释一个线程 大量python库为了省事依赖于这种机制 ---》 python多线程效率低 GIL 即为从python 解释器由于上锁带了的同一时刻只能解释一个线程的问题 解决方案 : * 不使用线程 转而使用进程 * 不实用c作为解释器 java c#都可以做python解释器 IO 密集型 程序中进行了大量IO操作,只有少量的CPU操作 在内存中进行了数据的交换的操作都可以认为是IO操作 特点 : 速度较慢,使用cpu不高 cpu密集型(计算密集型):大量的程序都在进行运算操作 特点 : cup占有率高 效率测试: Line cpu 1.224205732345581 Line IO 4.142379522323608 Thread cpu 0.7009162902832031 Thread IO 3.458016872406006 Process cpu 0.6419346332550049 Process IO 1.8482108116149902 * 多线程的工作效率和单线程几乎相近,而多进程要比前两者有明显的效率提升 设计模式 设计模式代表了一种最佳实践,是被开发人员长期开发总结,用来解决某一类问题的思路方法。这些方法保证了代码的效率也易于理 解。 单例模式 工厂模式 生产者模式。。。。 生产者消费者模式 高内聚 : 在同一模块内,实现单一功能,尽量不使功能混杂 低耦合 : 不同的模块之间尽量相互独立,减少模块间的影响 总结 1.进程和线程的区别 2.会创建使用线程 threading 3.掌握基本的线程间同步互斥编程方法 4.知道什么是GIL 5.了解设计模式的概念关于进程,线程,多进程和多线程的网络编程 (2)
内容版权声明:除非注明,否则皆为本站原创文章。