什么是多任务?
同时运行多个程序,比如:一边下载视频,一边听歌,一边使用浏览器
进程
什么是进程
- 计算机中已经运行程序的实体
- 运行在内存中活的程序
什么是程序
存在硬盘中的死的二进制文件
进程调度
- 时间片轮转:CPU给进程一个很小的时间段,程序交替执行
- 并发:在同一时刻,只有一个进程在运行,交替运行,时间片轮转算法
- 并行:在同一时刻,多个进程同时进行。
Linux中的fork
实例代码
- 注意:fork只可以在Linux下使用
- 语法代码
import os
# pid 是当前进程的子进程PID
# os.getpid() 是当前进程的进程号
# os.getppid() 是其父进程的进程号
# 子进程永远返回0,而父进程返回子进程的ID。
pid = os.fork()
if pid == 0:
print("子进程")
print(f"pid:{pid} 猜猜我会出现几次 当前进程号: {os.getpid()} 父进程:{os.getppid()}")
else:
print("父进程")
print(f"pid:{pid} 猜猜我会出现几次 当前进程号: {os.getpid()} 父进程:{os.getppid()}")
- 程序执行到os.fork()时,操作系统会创建一个新的进程(子进程),然后复制父进程的所有信息到子进程中。
- 父进程和子进程都会从fork()函数中得到一个返回值,在子进程中这个值一定是0,而父进程中是子进程的id号。
多进程修改全局变量
import os
a = 0
pid = os.fork()
if pid == 0:
a += 1
print(f"子进程:{a}")
else:
a += 1
print(f"父进程:{a}")
总结:多进程中,每个进程中所有数据(包括全局变量)都各有拥有一份,互不影响
多次fork问题
# coding=utf-8
import os
import time
# 注意,fork函数,只在Unix/Linux/Mac上运行,windows不可以 6
pid = os.fork()
if pid == 0:
print('hello 1')
else:
print('hello 2')
pid = os.fork()
if pid == 0:
print('hello 3')
else:
print('hello 4')
time.sleep(1)
注意:父进程、子进程执行顺序没有规律,完全取决于操作系统的调度算法
python多进程编程
Process类
import time
from multiprocessing import Process
def sing():
for _ in range(5):
print("唱歌")
time.sleep(1)
def dance():
for _ in range(5):
print("跳舞")
time.sleep(1)
def main():
# 创建对象
p1 = Process(target=sing)
p2 = Process(target=dance)
# 启动进程实例
p1.start()
p2.start()
# 等待子进程结束
p1.join()
p2.join()
if __name__ == '__main__':
# 开始时间
time_start = time.time()
main()
# 结束时间
time_end = time.time()
print(f"总输出时间:{time_end-time_start}")
Process([group [, target [, name [, args [,kwargs]]]]])
- target:表示这个进程实例所调用对象;
- args:表示调用对象的位置参数元组;
- kwargs:表示调用对象的关键字参数字典;
- name:为当前进程实例的别名;
- group:大多数情况下用不到;
Process类常用方法:
- is_alive():判断进程实例是否还在执行;
- join([timeout]):是否等待进程实例执行结束,或等待多少秒;
- start():启动进程实例(创建子进程);
- run():如果没有给定target参数,对这个对象调用start()方法时,就将执行对象中的run()方法;
- terminate():不管任务是否完成,立即终止;
Process类常用属性:
- name:当前进程实例别名,默认为Process-N,N为从1开始递增的整数;
- pid:当前进程实例的PID值;
join()方法可以等待子进程结束后再继续往下运行,通常用于进程间的同步。
Process类继承
from multiprocessing import Process
class ProcessClass(Process):
def __init__(self, name):
# 直接调用父类的构造方法,下面两种方法都可以
Process.__init__(self)
# super(ProcessClass, self).__init__()
# 初始化赋值
self.name = name
def run(self):
print("我是Process类的继承", self.name)
# 创建一个子进程
p1 = ProcessClass("江小白")
# 启动子进程
p1.start()
进程池
import time
from multiprocessing.dummy import Pool
def work(num):
print("当前num", num)
time.sleep(1)
# 创建池子
p = Pool(5)
for i in range(30):
p.apply_async(work, args=(i,))
# 关闭以后不可以再添加任务,但是以前的任务会执行完
p.close()
# 等待结束
p.join()
进程间通信
import time
from multiprocessing import Queue, Process
# 写入函数
def write(queue):
# li = ['a', 'b', 'c', 'd', 'e', 'f']
for i in ['a', 'b', 'c', 'd', 'e', 'f']:
# 把i放到队列里
queue.put(i)
# print(f"把{i}放到队列中")
print("把", i, "放到队列中")
# 睡眠
# time.sleep(1)
# 读出函数
def read(queue):
while 1:
print("读出:", queue.get(), "数量", queue.qsize())
# 创建队列
q = Queue(3)
# 创建进程
p1 = Process(target=write, args=(q,))
p2 = Process(target=read, args=(q,))
# 启动进程
p1.start()
p2.start()
# 等待进程
p1.join()
p2.join()
Queue的使用
- Queue.qsize(): 返回当前队列包含的消息数量;
- Queue.empty(): 如果队列为空,返回True,反之False ;
- Queue.full(): 如果队列满了,返回True,反之False;
- Queue.get([block[, timeout]]):获取队列中的一条消息,然后将其从列队中移除,block默认值为True;
1)如果block使用默认值,且没有设置timeout(单位秒),消息列队如果为空,此时程序将被阻塞(停在读取状态),直到从消息列队读到消息为止,如果设置了timeout ,则会等待timeout秒,若还没读取到任何消息,则抛出"Queue.Empty"异常;
2)如果block值为False,消息列队如果为空,则会立刻抛出"Queue.Empty"异常;
Queue.get_nowait():相当Queue.get(False); - Queue.put(item,[block[, timeout]]):将item消息写入队列,block默认值为True;
1)如果block使用默认值,且没有设置timeout(单位秒),消息列队如果已经没有空间可写入,此时程序将被阻塞(停在写入状态),直到从消息列队腾出空间为止,如果设置了timeout,则会等待timeout秒,若还没空间,则抛出"Queue.Full"异常;
2)如果block值为False,消息列队如果没有空间可写入,则会立刻抛出"Queue.Full"异常; - Queue.put_nowait(item):相当Queue.put(item, False);
进程间的数据相互独立
其它补充
孤儿进程
就是父进程先于子进程死的时候,子进程就变成孤儿进程,会认1号进程为干爹
僵尸进程
子进程死了,父进程还没来得急给儿子收尸,子进程就会成为僵尸进程
如何清理僵尸进程:
- ps -ef | grep "def" 这个出来就是僵尸进程
- kill -9 父进程
杀死僵尸进程的父进程,僵尸进程就会变成孤儿进程
守护进程
当成为孤儿进程后,会被1号进程收养,这个孤儿进程将不会被结束掉,成为守护进程
网友评论