进程

概念:
进程:编写玩的程序没有运行称为程序,正在运行着称为进程
os.fork程序及说明
import os
# 注意,fork函数,只在Unix/Linux/Mac上运行,windows不可以
pid = os.fork()
if pid == 0:
print('哈哈1')
else:
print('哈哈2')
说明:
- 程序执行到os.fork()时,操作系统会创建一个新的进程(子进程),然后复制父进程的所有信息到子进程中
- 然后父进程和子进程都会从fork()函数中得到一个返回值,在子进程中这个值一定是0,而父进程中是子进程的 id号
- fork返回值 子进程中为0,父进程中为子进程的进程id码 返回值小于0时 创建进程失败
- os.gepid当前进程,,,os.getppid。获取父进程的id
fork和process的区别:
- 用fork产生的子进程,父进程结束之前是可以输入的,父进程结束,子进程成为孤儿进程,此时子进程当中的input输入会报错---原因是当成为孤儿进程时交给了后台进程处理,
- process产生的子进程中遇到input输入直接就报错,关闭了input的操作
僵尸进程-孤儿进程
- 僵尸进程:子进程先结束,父进程没有结束,并且父进程也没有回收子进程的资源,此时会产生僵尸进程
- 孤儿进程:父进程结束,子进程没有结束,此时产生孤儿进程,此时的孤儿进程被特殊 进程收养
- os.wait()会等待子进程结束并回收资源---不会产生僵尸进程
- process.join()会等待子进程结束并回收资源---不会产生僵尸进程
- process.start()会回收僵尸进程的资源
- 主进程结束会将僵尸进程的资源回收
在Unix/Linux操作系统中,提供了一个fork()系统函数,它非常特殊。
普通的函数调用,调用一次,返回一次,但是fork()调用一次,返回两次,因为操作系统自动把当前进程(称为父进程)复制了一份(称为子进程),然后,分别在父进程和子进程内返回。
子进程永远返回0,而父进程返回子进程的ID。
这样做的理由是,一个父进程可以fork出很多子进程,所以,父进程要记下每个子进程的ID,而子进程只需要调用getppid()就可以拿到父进程的ID。
代码:
import os
#
kpid = os.fork()
if kpid < 0:
print('创建失败')
elif kpid == 0:
print('我是子进程{},我的父进程为{}'.format(os.getpid(), os.getppid()))
else:
print('我是父进程{},我的子进程为{}'.format(os.getpid(), kpid))
print('父子进程都能执行到')
运行结果
我是父进程10213,我的子进程为10217 父子进程都能执行到 我是子进程10217,我的父进程为10213 父子进程都能执行到
多进程之间修改全局变量
代码
import os
num = 1
kpid = os.fork()
if kpid < 0:
print('创建失败')
elif kpid == 0:
num += 1
print(num)
print('我是子进程{},我的父进程为{}'.format(os.getpid(), os.getppid()))
else:
num += 1
print(num)
print('我是父进程{},我的子进程为{}'.format(os.getpid(), kpid))
print('父子进程都能执行到')
结果:
2 我是子进程10254,我的父进程为10253 父子进程都能执行到 2 我是父进程10253,我的子进程为10254 父子进程都能执行到
- 多进程中,每个进程中所有数据(包括全局变量)都各有拥有一份,互不影响
- 进程之间从逻辑上是隔离的不能通过全局变量传递数据,,全局变量也会被复制一份
两句fork的理解

父子进程的执行顺序
父进程、子进程执行顺序没有规律,完全取决于操作系统的调度算法
multiprocessing
此模块在windowns下也可以执行
Process的基本用法
import os
from multiprocessing import Process
def run_proc(name):
print('子进程执行中{},pid={}'.format(name, os.getpid()))
if __name__ == '__main__':
print('父进程开始执行{}'.format(os.getpid()))
p = Process(target=run_proc, args=('test',))
print('子进程开始执行')
p.start()
p.join()
print('end')
说明
- 创建子进程时,只需要传入一个执行函数和函数的参数,创建一个Process实例,用start()方法启动,这样创建进程比fork()还要简单。
- join()方法可以等待子进程结束后再继续往下运行,通常用于进程间的同步。
Process语法结构如下:
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值;
继承Process类
import os from multiprocessing import Process import time class MyProcess(Process): def __init__(self, stop_time, name): Process.__init__(self) self.name = name self.stop_time = stop_time def run(self): print('{}子进程开始运行id为{}'.format(self.name,os.getpid())) print('正在运行中') time.sleep(self.stop_time) print('子进程执行结束') if __name__ == '__main__': print('主进程开始执行{}'.format(os.getpid())) p = MyProcess(3, 'P1') # 对一个不包含target属性的Process类执行start()方法,就会运行这个类中的run()方法,所以这里会执行p1.run() p.start() p.join() print('主进程结束')
继承类Process 类之后,里面的run方法会在创建进程并启动会自动调用 如果手动在main方法里面调用那是父进程在执行 并没有让创建的进程执行
进程池
import os
import random
from multiprocessing import Pool
import time
print('主进程开始执行id为{}'.format(os.getpid()))
# 1.创建进程执行体
def run_func(name):
print('{}开始执行进程号为{}'.format(os.getpid(), name))
start_time = time.time()
time.sleep(3)
end_time = time.time()
print('{}耗时{}'.format(name,end_time-start_time))
print('{}结束执行进程号为{}'.format(os.getpid(), name))
# 2.创建进程池
my_pool = Pool(1) # 注意定义进程池的位置,定义在任务执行体的后面
# 3.向进程池当中添加任务
for i in range(1,10):
my_pool.apply_async(run_func, args=('Process-{}'.format(i),))
print('添加完成')
# 4.关闭进程池
my_pool.close()
# 5.阻塞进程池
my_pool.join()
print('主进程执行结束')
Poll返回的参数说明:
multiprocessing.Pool常用函数解析:
-
apply_async(func[, args[, kwds]]) :使用非阻塞方式调用func(并行执行,堵塞方式必须等待上一个进程退出才能执行下一个进程),args为传递给func的参数列表,kwds为传递给func的关键字参数列表;
-
apply(func[, args[, kwds]]):使用阻塞方式调用func
-
close():关闭Pool,使其不再接受新的任务;
-
terminate():不管任务是否完成,立即终止;
-
join():主进程阻塞,等待子进程的退出, 必须在close或terminate之后使用;
进程之间的队列
Queue的使用
可以使用multiprocessing模块的Queue实现多进程之间的数据传递,Queue本身是一个消息列队程序
消息队列的对象方法说明:
初始化Queue()对象时(例如:q=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);
Queue实例
以Queue为例,在父进程中创建两个子进程,一个往Queue里写数据,一个从Queue里读数据:
1 import os 2 from multiprocessing import Queue,Process 3 4 # 创建通信队列 5 import time 6 7 my_q = Queue(10) 8 9 # 创建执行的函数 10 # 写 11 def write(q): 12 for i in ['wang', 'xiao', 'fei']: 13 q.put(i) 14 time.sleep(0.5) 15 print('{}进程成功写入{}'.format(os.getpid(),i)) 16 print('写入结束') 17 # 读 18 def read(q): 19 while 1: 20 if not q.empty(): 21 msg = q.get() 22 print('{}成功读取{}'.format(os.getpid(),msg)) 23 time.sleep(0.5) 24 else: 25 print('{}进程执行结束'.format(os.getpid())) 26 break 27 28 print("---start----") 29 # 创建两个进程 负责读取,与写入 30 p_w = Process(target=write, args=(my_q,)) 31 p_r = Process(target=read, args=(my_q,)) 32 p_w.start() 33 p_w.join() # 等待全部写入后开始读取 34 p_r.start() 35 p_r.join() 36 print("---end----")
进程池中的Queue
1 import os 2 from multiprocessing import Pool, Manager 3 4 import time 5 6 7 def write_task(my_queue): 8 for i in ("hello", "word", "prthon", "#end#"): 9 my_queue.put(i) 10 print("%s进程已添加任务%s" % (os.getpid(), i)) 11 time.sleep(1) 12 13 print("%s进程已结束,,,," % os.getpid()) 14 15 16 def read_task(my_queue): 17 while True: 18 msg = my_queue.get() 19 20 if msg == "#end#": 21 print("%s进程读取结束......" % os.getpid()) 22 break 23 24 print("%s进程读取到的内容是%s" % (os.getpid(), msg)) 25 time.sleep(1) 26 27 28 def main(): 29 # 创建进程池 30 my_pool = Pool(2) 31 32 # 创建队列 33 my_queue = Manager().Queue() # 使用进程池创建的进程共享数据时必须使用该方法创建队列 34 35 # 向进程池里面添加任务 36 my_pool.apply_async(write_task, args=(my_queue,)) 37 my_pool.apply_async(read_task, args=(my_queue,)) 38 39 my_pool.close() 40 my_pool.join() # 41 print("%s主进程任务结束" % os.getpid()) 42 43 44 if __name__ == "__main__": 45 main()

浙公网安备 33010602011771号