Python进程的基本使用

Python 进程的使用

一、进程的简述

1.进程的介绍

在Python程序中,想要实现多任务可以使用进程来完成,进程是实现多任务的一种方式

2.进程的概念

一个正在运行的程序或者软件就是一个进程,他是操作系统进程资源分配分基本单位。也就是说每启动一个进程,操作系统都会给其分配一定的运行资源 (内存资源)保证进程的运行。

3.进程的特征
  1. 动态性;
  2. 并发性;
  3. 独立性;
  4. 异步性;
  5. 结构特征;

多个不同的进程可以包含相同的程序:一个程序在不同的数据集里是构成不同的进程,能得到不同的结果;但是执行过程中,程序不能发生改变
注意:同一个程序执行两次,就会在操作下同中出现两个进程,所以我们可以同事运行一个软件,分别做不同的事情也不会混乱

进程与线程的关系:
一个程序运行后至少有一个进程,一个进程默认有一个线程,进程里面可以创建多个线程,线程是依附在进程里面的,没有进程就没有线程。

二、并发与并行,同步与异步

  1. 并发: 伪并行,假的。就是几个进程共用一个cpu,几个进程之间是时间轮换的,而这个轮换的时间又很短,肉眼是看不到的,所以我们肉眼看到的是几个进程同时运行。线程就是并发。
  2. 并行:指一个进程就用一个CPU,每个进程都没有停止,就是一起运行,同时运行。
  3. 同步: 一个任务执行完毕后,另一个任务才开始执行。
  4. 异步:两个任务之间没有关联,你执行你的,我执行我的,互相不用等待。

三、进程的使用

1. 创建进程
# 导入进程包
import multiprocessing
# 创建子进程
process = multiprocessing .Process(group, target, args,kwargs,name)
2. Process 进程类参数说明
  • group:指定进程组,多数情况下用不到,目前只能使用None;
  • target:表示调用对象,即子进程要执行的任务 ;
  • name:子进程的名称,子进程的别名,可以不设定;
  • args:给target指定的函数传递的参数,以元组的方式;
  • kwargs:给target指定的函数传递的参数,以字典方式;
Process 创建的实例对象的常用方法
  • start():启动子进程实例(创建子进程),并调用该子进程中 run()放法;
  • join(timeout):主进程等待子进程执行结束再结束,timeout是可选的超时时间;
  • is_alive():判断进程是否还存活
  • run():进程启动是运行的方法,正是它调用的target指定的函数
  • terminate():不管任务是否完成,立即终止子进程;
Process 创建的实例对象的常用属性
  • name:当前进程的别名。默认为Process-N,N为从1开始递增的证书;
  • pid:当前进程的pid(进程号)
3. 代码实例
import multiprocessing
import os
import time


def fun1(name):
    print('子进程fun1正在运行。。。。。')
    time.sleep(0.5)
    print('我的名字是:',name)
    # 获取进程的名称
    print('子进程name:',multiprocessing.current_process())
    # 获取进程号pid
    print('子进程pid:',multiprocessing.current_process().pid, os.getpid())
    # 获取父进程的pid
    print('父进程pid: ',os.getppid())


if __name__ == '__main__':
    print('主进程启动')
    print('主进程name: ',multiprocessing.current_process())
    print('主进程pid:', multiprocessing.current_process().pid, os.getpid())

    # 创建进程
    p = multiprocessing.Process(group=None,target=fun1,args=('Tom',))
    p.start()  # 启动进程
    p.join()  # join()的作用是让主进程等子进程执行完再退出
    print('主进程结束')

运行结果:

主进程启动
主进程name: <_MainProcess(MainProcess, started)>
主进程pid: 11452 11452
子进程fun1正在运行。。。。。
我的名字是: Tom
子进程name: <Process(Process-1, started)>
子进程pid: 7792 7792
父进程pid: 11452
主进程结束

创建进程
import multiprocessing
import time


def drink():
    for i in range(3):
        print("喝汤。。。")
        time.sleep(1)


def eat():
    for i in range(3):
        print("吃饭。。。")
        time.sleep(1)


if __name__ == '__main__':
    drink_process = multiprocessing.Process(target=drink)
    eat_process = multiprocessing.Process(target=eat)
    drink_process.start()
    eat_process.start()
进程 传参数
import multiprocessing
import time


def drink(name):
    for i in range(3):
        print(f"{name}-喝汤。。。")
        time.sleep(1)


def eat(name):
    for i in range(3):
        print(f"{name}-吃饭。。。")
        time.sleep(1)


if __name__ == '__main__':
    drink_process = multiprocessing.Process(target=drink,args=("Tom",))
    eat_process = multiprocessing.Process(target=eat,args=("Mark~",))
    drink_process.start()
    eat_process.start()
全局变量在多个进程中不共享
  • 全局变量在多个进程中不共享,进程之间的数据是独立的,默认情况下互不影响。
import multiprocessing


# 定义全局变量
num = 100


def work1():
    print("work1正在运行......")
    global num   # 在函数内部声明使⽤全局变量num
    num = num + 1  # 对num值进⾏+1
    print("work1 num = {}".format(num))


def work2():
    print("work2正在运行......")
    print("work2 num = {}".format(num))
    

if __name__ == '__main__':
    # 创建进程p1
    p1 = multiprocessing.Process(group=None, target=work1)
    # 启动进程p1
    p1.start()
    # 创建进程p2
    p2 = multiprocessing.Process(group=None, target=work2)
    # 启动进程p2
    p2.start()
    p1.join()
    p2.join()
守护进程
  • 守护进程当主进程程序执行完毕后,守护进程随之结束
  • 守护进程内部允许再开子进程
import multiprocessing
import time


#把一个子进程设为守护进程之后,每当主进程执行完毕后,不管你子进程有没有执行完毕,都要跟着结束
def func1():
    print('func1正在运行')
    print('func1')
    time.sleep(4)
    print('func1运行完毕')


def func2():
    print('func2正在运行')
    print('func2')
    time.sleep(5)
    print('func2运行完毕')


if __name__ == '__main__':
    p1 = multiprocessing.Process(target=func1)
    p2 = multiprocessing.Process(target=func2)
    # 设置p1守护进程
    p1.daemon = True  # 在start之前,不然不会抛异常
    p1.start()
    p2.start()
    print('master end')

四、进程之间的通信

每个进程都有自己的用户空间,而内核空间是每个进程共享的,因此进程之间想要进行。

  1. 管道
  2. 消息队列
  3. 共享内存
  4. 信号量
  5. 信号
  6. Socket

五、队列

  • 队列就相当于一个容器,里面可以放数据,特点是先放进去先拿出来,即先进先出,
1.对列的基本语法
from multiprocessing import Queue  # 引入Queue模块
q = Queue(2)  # 创建一个对列对象,并给他设置容器大小,即能放几个数据
q.put(1)  # put() 方法是往容器里返数据
q.put(2)
q.put(3)  # 这里往容器里放第三个数据是,由于容器大小只有2,只能放连个,所以此处会阻塞,不会报错。
q.put_nowait(4)  # put_nowait() 这也是从容器里放数据,但如果容器满了,不会阻塞,会直接报错
q.get()
q.get()
q.get()  # 这是从容器里拿第三个数据,但是容器里的两个数据被拿完了,没有数据了,此时也会阻塞,不会报错
q.get(False)  #这也是从容器里拿数据的方法,当没数据时不会阻塞,直接报错
q.get_nowait()  #这也是从容器里拿数据的方法,当没数据时不会阻塞,直接报错

消费者生产者模型
def consumer(q):
    while True:
        res = q.get()
        if res is None:break
        time.sleep(random.randint(1, 3))
        print('%s 吃 %s' % (os.getpid(), res))


def producer(q):
    for i in range(10):
        time.sleep(random.randint(1, 3))
        res = "包子 %s" % i
        q.put(res)
        print('%s 生产了 %s' % (os.getpid(), res))
    q.put(None)  # 发送结束信号


if __name__ == '__main__':
    q = Queue()
    # 生产者们:即厨师们
    p1 = Process(target=producer, args=(q,))

    # 消费者们:即吃货们
    c1 = Process(target=consumer, args=(q,))

    # 开始
    p1.start()
    c1.start()
    p1.join()
    c1.join()
    print('主进程结束')
 
JoinableQueue
  • JoinableQueue 其实是一种队列,但他又比队列多两种办法,task_done()和 join()方法
from multiprocessing import Process, JoinableQueue
import time


def producer(q):  # 生产者
    for i in range(10):
        time.sleep(1)
        q.put(i)
        print('生产:',i)
    q.join()  # 当q收到的task_doned数量等于放进q的数据数量是,生产者就结束了


def consumer(q):  # 消费者
    while 1:
        time.sleep(1.5)
        ret = q.get()
        print('消费:', ret)
        q.task_done()  # 消费者每从q里取一个值,就像1返回一个task_done消息


if __name__ == '__main__':
    q = JoinableQueue()
    p = Process(target=producer, args=(q,))
    p.start()
    for i in range(3):
        p1 = Process(target=consumer, args=(q,))
        p1.daemon=True
        p1.start()
    p.join()

"""
整个过程就是;生产者生产了10个包子,三个消费者吃包子,每吃一个包子;往q里面发一个task_done,
当q拥有10个task_done时,意味着是个包子吃完了,此时,生产者结束了,接着主进程也结束了,然后
守护进程也结束了,即所有的消费者子进程结束。
"""

六、进程池

1. apply

apply方法是阻塞的。
意思是等待当前子进程执行完毕后,在执行下一个进程
以为apply是阻塞的,所以进入子进程执行后,等待当前子进程执行完毕后,在继续执行下一个进程
例如:有三个进程0.1.2.等待进程0执行完毕后,在执行子进程1.然后再执行子进程2.最后回到主进程,执行主进程剩余部分,就像上面的执行结果一样。
这样好像跟单进程串行执行没什么区别了。

def func(msg):
    print('msg:',msg)
    time.sleep(3)
    print('end')


if __name__ == '__main__':
    pool = multiprocessing.Pool(processes=3)
    start_time = time.time()
    for i in range(3):
        msg = 'hello %s' % i
        pool.apply(func, (msg,))
    print('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~')
    pool.close()
    pool.join()
    print('sub-process(es) done.')
    print('主进程结束 耗时:%s' % (time.time() - start_time))
2.apply_async

apply_async 是一步非阻塞的。
意思就是,不用等待当前进程执行完毕,随时根据系统调度进行进程切换。

def say(msg):
    print('msg: %s' % msg)
    time.sleep(2)
    print('end')

if __name__ == '__main__':
    print("开始执行主进程")
    start_time = time.time()
    # 使用进程池建立三个子进程
    pool = multiprocessing.Pool(3)
    print('开始执行三个子进程')
    for i in range(3):
        pool.apply_async(say,[i])
    print('主进程结束  耗时 %s' % (time.time() - start_time))
 """
 执行结果: 完全没有等待子进程执行完毕,主进程就已经执行完毕,并退出程序
        开始执行主进程
        开始执行三个子进程
        主进程结束  耗时 0.08800506591796875
 """

为什么会这样呢?

因为进程的切换是操作系统来控制的,抢占式的切换模式。
我们首先运行的是主进程,cpu运行很快啊,这短短的几行代码,完全没有给操作系统进程切换的机会,
主进程就运行完毕了,整个程序结束,子进程完全没有机会切换到程序就已经技术了
apply是阻塞式的。
首先主进程开始运行,碰到子进程后,操作系统切换到子进程,等待子进程运行结束后,在切换到另一个子进程,
直到所有子进程运行完毕,然后再切换到主进程,运行剩余的部分。
apply_async是异步非阻塞式的
首先进程开始运行,碰到子进程后,主进程说;让我先运行个够,等到操作系统进行进程切换的时候
在交给子进程运行。以为我们的程序太短,然而还没等到操作系统进行进程切换,主进程就运行完毕了。
想要子进程执行,就要告诉主进程;你等着所有子进程执行完毕后,在运行剩余部分
pool.close()
pool.join()
这两句话是告诉主进程,你等着所有子进程运行完毕后在运行剩余部分。
注意:close 必须在join前调用

def say_join(msg):
    print('msg: %s' % msg)
    time.sleep(2)
    print('end')


if __name__ == '__main__':
    print('开始执行主进程')
    start_time = time.time()
    pool = multiprocessing.Pool(3)
    print('开始执行三个子进程')
    for i in range(3):
        pool.apply_async(say_join, [i,])
    pool.close()  # 关闭Pool,使其不在接受新的任务
    pool.join()  # 主

    print('主进程结束 耗时:%s' % (time.time() - start_time))

"""
运行结果:
            开始执行主进程
            开始执行三个子进程
            msg: 0
            msg: 1
            msg: 2
            end
            end
            end
            主进程结束 耗时:2.4161384105682373
"""
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值