软件测试Python之进程

Python之进程

>什么是进程

在一个正在运行的程序中,代码和调度的资源称为进程,进程是操作系统分配资源的基本单元 之前有提到过,多任务不仅线程可以完成,进程也可以

>进程的状态

现实情况里,我们电脑的任务数通常是大于CPU核数,这样就会导致一些任务正在执行中,而一些任务在等待执行,这样就会导致不同的状态

 

就绪态:运行的条件已经满足,正在等待CPU执行

 

运行态:CPU正在执行

等待态:等待一些满足的条件,条件满足后进入就绪态

>进程的创建multiprocessing

multiprocessing模块提供了一个Process类来代表一个进程对象

-使用主进程创建一个子进程

# -*- coding:utf-8 -*-
import multiprocessing
import time

def run_proc():
    """子进程要执行的代码"""
    while True:
        print("这是子进程...1")
        time.sleep(2)

if __name__=='__main__':
    p = multiprocessing.Process(target=run_proc)
    p.start()
    while True:
        print("这是主进程...2")
        time.sleep(2)
复制代码
免费领取Python自动化学习资料  工具,面试宝典面试技巧,加QQ群,785128166,群内还会大佬技术交流

执行结果

这是主进程...2
这是子进程...1
这是主进程...2
这是子进程...1
这是主进程...2
.
.
复制代码

小结

  • 进程的创建和执行和线程传入参数的方式有些类似,都是调用start()方法启动

>进程中的PID

# -*- coding:utf-8 -*-
from multiprocessing import Process
import os
import time

def run_proc():
    """子进程要执行的代码"""
    print('子进程运行中,pid=%d...' % os.getpid())  # os.getpid获取当前进程的进程号
    print('子进程将要结束...')

if __name__ == '__main__':
    print('父进程pid: %d' % os.getpid())  # os.getpid获取当前进程的进程号
    p = Process(target=run_proc)
    p.start()
复制代码

执行结果

父进程pid: 15128
子进程运行中,pid=4868...
子进程将要结束...
复制代码

>Process类的属性和方法

Process([group [, target [, name [, args [, kwargs]]]]])

  • target:如果传递了函数的引用,可以任务这个子进程就执行这里的代码
  • args:给target指定的函数传递的参数,以元组的方式传递
  • kwargs:给target指定的函数传递命名参数
  • name:给进程设定一个名字,可以不设定
  • group:指定进程组,大多数情况下用不到

Process创建的实例对象的常用方法

  • start():启动子进程实例(创建子进程)
  • is_alive():判断进程子进程是否还在活着
  • join([timeout]):是否等待子进程执行结束,或等待多少秒
  • terminate():不管任务是否完成,立即终止子进程

Process创建的实例对象的常用属性

  • name:当前进程的别名,默认为Process-N,N为从1开始递增的整数
  • pid:当前进程的pid(进程号)

>给子进程指定的函数传递参数

免费领取Python自动化学习资料  工具,面试宝典面试技巧,加QQ群,785128166,群内还会大佬技术交流

# -*- coding:utf-8 -*-
from multiprocessing import Process
import os
from time import sleep


def run_proc(name, age, **kwargs):
    for i in range(10):
        print('子进程运行中,name= %s,age=%d ,pid=%d...' % (name, age, os.getpid()))
        print(kwargs)
        sleep(0.2)

if __name__=='__main__':
    p = Process(target=run_proc, args=('test',18), kwargs={"m":20})
    p.start()
    sleep(1)  # 1秒中之后,立即结束子进程
    p.terminate()
    p.join()
复制代码

执行结果

子进程运行中,name= test,age=18 ,pid=14828...
{'m': 20}
子进程运行中,name= test,age=18 ,pid=14828...
{'m': 20}
子进程运行中,name= test,age=18 ,pid=14828...
{'m': 20}
子进程运行中,name= test,age=18 ,pid=14828...
{'m': 20}
子进程运行中,name= test,age=18 ,pid=14828...
{'m': 20}
复制代码

>进程间不共享全局变量

import multiprocessing
import time
import os

nums = [11,22]

def sub_process1():
	print("its sub_process1,pid:%s;nums:%s" % (os.getpid(),nums))
	nums.append(33)
	time.sleep(1)
	print("its sub_process1,pid:%s;nums:%s" % (os.getpid(),nums))

def sub_process2():
	print("its sub_process2,pid:%s;nums:%s" % (os.getpid(),nums))

if __name__ == '__main__':
	p1 = multiprocessing.Process(target=sub_process1)
	p1.start()
	p1.join()

	p2 = multiprocessing.Process(target=sub_process2)
	p2.start()
复制代码

执行结果

its sub_process1,pid:5028;nums:[11, 22]
its sub_process1,pid:5028;nums:[11, 22, 33]
its sub_process2,pid:6400;nums:[11, 22]
复制代码

>进程、线程

进程是系统进行资源分配和调度的一个独立单位

线程是进程的一个实体,是CPU调度和分配的基本单位,它是比进程更小的独立运行的基本单位.线程基本不拥有系统资源,只拥有一些必不可少的资源(程序计数器,寄存器,栈等),线程可与同属一个进程的其它线程共享进程所拥有的全部资源

二者区别

一个程序至少有一个进程,一个进程至少有一个线程

线程的划分尺度(资源)小于进程,使得线程的并发性高

进程在运行过程中独享内存,而线程是共享的,极大的提高了程序的运行效率

小结

线程的资源开销小,但是资源的管理和保护不如进程

>进程间通信Queue

Queue()实例化时,如果没有指定最大可接收消息的最大参数或者为负值,那么默认没有上限(直到内存的上限)

  • Queue.qsize(): 返回当前队列包含消息的数量
  • Queue.empty(): 判断队列是否为空,是为True,否为False
  • Queue.full(): 判断队列是否满了,是为True,否为False
  • Queue.get([block[,timeout]]): 获取一条队列的消息,然后将其从队列中删除,black默认为True
    • 如果block为默认值,没有设置timeout,消息队列又为空,那么此时程序将会阻塞(停在读取状态),直到队列中有值,从消息队列中读取到值.如果设置了timeout,等待timeout的时间就会报出(Queue.Empty)异常
    • 如果block为False,消息队列为空,则会立刻抛出(Queue.Empty)异常
  • Queue.get_nowait(): 相当于Queue.get(False)
  • Queue.put(item,[block[,timeout]]): 将item写入队列,block默认为True
    • 如果block为默认值,没有设置timeout,在写入时没有空间了,那么程序会阻塞(停止在写入状态),直到有空间写入为止.如果设置了timeout,在写入时没有空间时,会等待timeout时间,就会抛出Queue.Full异常
    • 如果block为False,在向消息队列写入时没有空间了,直接抛出Queue.Full异常
  • Queue.put_nowait(): 相当于Queue.put(block=False)

>Queue操作

from multiprocessing import Queue

q = Queue(3) #在实例化时传入消息队列的最大数量
q.put("test1")
q.put("test2")
print(q.full()) #这里队列未满,返回False
q.put("test3")
print(q.full()) #这里队列已经满了,返回True

try:
	q.put("test4", timeout=2) #在写入时,如果队列是满的,就等待两秒钟,如还不可以,抛出异常
except:
	print("当前消息队列的数量为%s" % q.qsize())

try:
	q.put("test4", block=False) # 在写入时,如果队列是满的,直接抛出异常
except:
	print("当前消息队列的数量为%s" % q.qsize())

# 在写入之前,可以判断下队列是否是满的
if not q.full():
	q.put_nowait("test4")

# 同理,在取数之前,判断队列是否为空
if not q.empty():
	for i in range(q.qsize()):
		print(q.get_nowait())
复制代码

执行结果

False
True
当前消息队列的数量为3
当前消息队列的数量为3
test1
test2
test3
复制代码

>两个进程分别读写Queue

from multiprocessing import Process,Queue
import time
import random

def write(q):
	nums = [11,22,33]
	for num in nums:
		if not q.full():
			print("write into num:%s" % num)
			q.put(num)
			time.sleep(random.random())
def read(q):
	while True:
		if not q.empty():
			num = q.get(True)
			print("read data num:%s" % num)
			time.sleep(random.random())
		else:
			break

if __name__ == '__main__':
	q = Queue()
	p1 = Process(target=write, args=(q,))
	p2 = Process(target=read, args=(q,))

	p1.start()
	p1.join()

	p2.start()
	p2.join()

	print("所有的数据都读写完毕了...")
复制代码

执行结果

write into num:11
write into num:22
write into num:33
read data num:11
read data num:22
read data num:33
所有的数据都读写完毕了...
复制代码

>multiprocess Pool(在linux环境可以正常执行,windows有异常)

为什么要引用进程池,当我们需要创建少量的进程时,可以手动的去创建,可是需要的创建的进程数量多时,就可以用到multiprocess中的Pool方法

我们在初始化Pool时,可以制定一个参数(最大的进程数),当有新的请求到Pool时,如果Pool池还没满,就会添加,如果满了,就会等到Pool池中有进程结束,才会用之前的进程来执行这个请求

multiprocess.Pool方法解析

  • apply_async(func[,args[,kwargs]]): 使用非阻塞方式调用func(并行执行,阻塞的情况是等待上一个进程结束才能执行下一个请求),args为传入的参数,kwargs为关键字参数列表
  • close(): 关闭Pool,使其不再结束新的任务
  • terminate(): 不管任务是否完成,都立即退出
  • join(): 主进程阻塞,等待子进程,在close()和terminate()后执行
# -*- coding:utf-8 -*-
from multiprocessing import Pool
import os, time, random

def work(msg):
	t_start = time.time()
	print("执行开始,进程的ID号为:%s" % os.getpid())
	time.sleep(random.random()*2)
	t_send = time.time()
	print("执行耗费时间为:%s" % (t_start - t_send))

po = Pool(3)

for i in range(0, 10):
	po.apply_async(work, (i,))

print("programmer starting....")
po.close()
po.join()
print("programmer ending...")
复制代码

执行结果

programmer starting....
执行开始,进程的ID号为:24832
执行开始,进程的ID号为:24831
执行开始,进程的ID号为:24833
执行耗费时间为:-0.21535086631774902
执行开始,进程的ID号为:24832
执行耗费时间为:-1.3048121929168701
执行开始,进程的ID号为:24833
执行耗费时间为:-1.4840171337127686
执行开始,进程的ID号为:24831
执行耗费时间为:-1.6602394580841064
执行开始,进程的ID号为:24832
执行耗费时间为:-0.48267197608947754
执行开始,进程的ID号为:24831
执行耗费时间为:-0.277604341506958
执行开始,进程的ID号为:24831
执行耗费时间为:-0.2472696304321289
执行开始,进程的ID号为:24831
执行耗费时间为:-1.3967657089233398
执行耗费时间为:-0.2590181827545166
执行耗费时间为:-1.4253907203674316
programmer ending...
复制代码

>进程池中的进程通信(Queue)

使用进程池Pool时,不能使用multiprocess.Queue,应该使用multiprocess.Manager()中的Queue 实战演示

# -*- coding:utf-8 -*-
from multiprocessing import Manager,Pool
import os,time,random

def reader(q):
    print("reader启动(%s),父进程为(%s)" % (os.getpid(), os.getppid()))
    for i in range(q.qsize()):
        print("reader从Queue获取到消息:%s" % q.get(True))

def writer(q):
    print("writer启动(%s),父进程为(%s)" % (os.getpid(), os.getppid()))
    for i in "itcast":
        q.put(i)

if __name__=="__main__":
    print("(%s) start" % os.getpid())
    q = Manager().Queue()
    po = Pool()
    po.apply_async(writer, (q,))

    time.sleep(1)  # 先让上面的任务向Queue存入数据,然后再让下面的任务开始从中取数据

    po.apply_async(reader, (q,))
    po.close()
    po.join()
    print("(%s) End" % os.getpid())
复制代码

执行结果

(7740) start
writer启动(7448),父进程为(7740)
reader启动(8096),父进程为(7740)
reader从Queue获取到消息:i
reader从Queue获取到消息:t
reader从Queue获取到消息:c
reader从Queue获取到消息:a
reader从Queue获取到消息:s
reader从Queue获取到消息:t
(7740) End
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值