Python——进程


进程

什么是操作系统:
  • 操作系统(简称OS):是管理和控制计算机硬件与软件资源的计算机程序,是直接运行在“裸机”上的最基本的系统软件,任何其他软件都必须在操作系统的支持下才能运行
    操作系统可以使用户有一个好的工作环境,屏蔽硬件物理特性和操作细节,为用户使用计算机提供了便利。同时为其它软件的开发提供必要的服务和相应的接口等
  • 操作系统管理着计算机硬件资源,同时按照应用程序的资源请求,分配资源,如:划分CPU时间,内存空间的开辟,调用打印机等
生活中常见的操作系统:

windows
MacOS
android
ios

服务器端常用操作系统:服务器操作系统一般指的是安装在大型计算机上的操作系统,比如Web服务器、应用服务器和数据库服务器等

Unix
1965年之前的时候,电脑并不像现在一样普遍,不是一般人能接触到的,除非是军事或者学院的研究机构,而且当时大型主机至多能提供30台终端(30个键盘、显示器),连接一台电脑

为了解决数量不够用的问题:

  • 1965年左后由贝尔实验室、麻省理工学院以及通用电气共同发起了Multics项目,想让大型主机支持300台终端
  • 1969年前后这个项目进度缓慢,资金短缺,贝尔实验室退出了研究
  • 1969年从这个项目中退出的Ken
    Thompson当时在实验室无聊时,为了让一台空闲的电脑上能够运行“星际旅行”游戏,在8月份左右趁着其妻子探亲的时间,用了1个月的时间编写出了
    Unix操作系统的原型、
  • 1970年,美国贝尔实验室的 Ken Thompson,设计出很简单且很接近硬件的 B语言,并且他用B语言写了第一个UNIX操作系统
  • 因为B语言的跨平台性较差,为了能够在其他的电脑上也能够运行这个非常棒的Unix操作系统,Dennis Ritchie和Ken
    Thompson 从B语言的基础上准备研究一个更好的语言
  • 1972年,美国贝尔实验室的 Dennis Ritchie在B语言的基础上最终设计出了一种新的语言,C语言
  • 1973年初,C语言的主体完成。Thompson和Ritchie开始用它完全重写了现在的Unix操作系统
Minix
  • UNIX 是一个强大的多用户、多任务操作系统,但是因为政策改变,在Version 7
    Unix推出之后,发布新的使用条款,将UNIX源代码私有化,在大学中不再能使用UNIX源代码
  • 塔能鲍姆教授为了能在课堂上教授学生操作系统运作的细节,决定在不使用任何AT&T的源代码前提下,自行开发与UNIX兼容的操作系统,以避免版权上的争议。他以小型UNIX(mini-UNIX)之意,将它称为MINIX。
Linux
  • 因为Minix只是教学使用,因此功能并不强,因此Torvalds(林纳斯·托瓦兹)编写了Linux内核。严格来讲,Linux这个词本身只表示Linux内核,但在实际上人们已经习惯了用Linux来形容整个基于Linux内核的操作系统
多任务处理:使得计算机可以同时处理多个任务:

听歌的同时QQ聊天、办公、下载文件
实现方式:多进程、多线程
在这里插入图片描述

串行,并行,并发

并发:多个进程轮流执行,(多个程序来回的切换)cpu来回切换(单核CPU)
串行:如果有多个程序,完整执行一个,才能执行下一个。
并行:指多个进程同时执行(多核cpu)

程序:是一个指令的集合
进程:

  • 正在执行的程序;或者说:当你运行一个程序,你就启动了一个进程 编写完的代码,没有运行时,称为程序,正在运行的代码,称为进程
    程序是死的(静态的),进程是活的(动态的)
  • 操作系统轮流让各个任务交替执行 ,由于CPU的执⾏速度实在是太快了, 我们感觉就像所有任务都在同时执行一样
总结:

进程:程序运行的过程。
多进程中, 每个进程中所有数据(包括全局变量) 都各有拥有一份, 互不影响

多进程:

模拟多任务处理:一边唱歌一边跳舞
from time import sleep
def sing():
for i in range(3):
print("正在唱歌")
dance()
sleep(1)

def dance():
print("正在跳舞")
if name == 'main':
sing()

  • 程序开始运行时,首先会创建一个主进程

  • 在主进程(父进程)下,我们可以创建新的进程(子进程),子进程依赖于主进程,如果主进程结束,程序会退出

  • Python提供了非常好用的多进程包multiprocessing,借助这个包,可以轻松完成从单进程到并发执行的转换

from multiprocessing import Process 
def foo(): 
	print("in the foo") 
if __name__ == "__main__": 
	p = Process(target=foo)#args:参数以元组的形式存在
	p.start()#启动子进程,并且调用子进程的run方法,就绪状态
	p.join()# 主进程阻塞, 等待子进程的退出(主进程等待子进程终止)其中有参数代表超时时间,

解析:name == "main"

  • 在Windows中运行的时候,每创建一个进程,都在子进程所在的空间中导入主进程(所有的代码即
    import.py文件导入到子进程中,主进程有的子进程都有)如果把这一行去掉,即主进程代码中包含了子 进程创建对象,执行p =
    Process(target=foo)开辟空间,因为创建子进程,就会导入代码,就会再次
    创建自己就会形成递归,为了避免这个问题,把__name__ == "main"加上,
  • 为什么不出错了哪,因 为if做条件判断,只有if成立才会执行下面的代码,其中__name__存放的是当前文件的文件名,
    main(当前文件正在运行那么该文件名就是main)判断系统变量name是不是main,其中把.py文件当 做程序去运行一遍,即name=main,当前文件被执行的时候name的值是main,当被导入到其它文件中就不
    是main了,在子进程里.py文件是导入进来的,即在子进程里不成立,不执行了,避免了递归调用。
一个.py文件有两种使用方式 :

1.直接当成程序去运行。
2.作为模块导入到其它包里面。

总结:

  • if name == “main”:说明
    一个python的文件有两种使用的方法,第一是直接作为程序执行,第二是import到其他的python程序中被调用(模块重用)执行。
  • 因此if name == 'main': 的作用就是控制这两种情况执行代码的过程,name
    是内置变量,用于表示当前模块的名字 在if name == 'main':
    下的代码只有在文件作为程序直接执行才会被执行,而import到其他程序中是不会被执行的
  • 在 Windows 上,子进程会自动 import 启动它的这个文件,而在 import 的时候是会执行这些语句的。如果不加if
    name == "main":的话就会无限递归创建子进程 所以必须把创建子进程的部分用那个 if 判断保护起来 import 的时候 name 不是 main ,就不会递归运行了
Process(target , name , args) 参数介绍:
  • target表示调用对象,即子进程要执行的任务
  • args表示调用对象的位置参数元组,args=(1,)
  • name为子进程的名称

Process类常用方法:

  • p.start():启动进程,并调用该子进程中的p.run()
  • p.run():进程启动时运行的方法,正是它去调用target指定的函数,我们自定义类的类中一定要实现该方法
  • p.terminate()(了解)强制终止进程p,不会进行任何清理操作
  • p.is_alive():如果p仍然运行,返回True.用来判断进程是否还在运行
  • p.join([timeout]):主进程等待p终止,timeout是可选的超时时间

Process类常用属性:

  • name: 当前进程实例别名, 默认为Process-N, N为从1开始递增的整 数;
  • pid: 当前进程实例的PID值

全局变量在多个进程中不共享:进程之间的数据是独立的,默认情况下互不影响

from multiprocessing import Process
num = 1
def run1():
    global num
    num += 5
    print("子进程1运行中,num = %d"%(num))
def run2():
    global num
    num += 10
    print("子进程2运行中,num = %d"%(num)) 

创建进程的方式2:

创建新的进程还能够使用类的方式, 可以自定义一个类, 继承Process类, 每次实例化这个类的时候, 就等同于实例化一个进程对象

import multiprocessing
import time
class ClockProcess(multiprocessing.Process):
def run(self):
        n = 5
        while n > 0:
            print(n)
            time.sleep(1)
            n -= 1
if __name__ == '__main__':
    p = ClockProcess()
    p.start()
    p.join()

进程池:

进程池:用来创建多个进程

  • 当需要创建的子进程数量不多时, 可以直接利⽤multiprocessing中的Process动态生成多个进程,
    但如果是上百甚至上千个目标, 手动的去创建进程的工作量巨大, 此时就可以用到multiprocessing模块提供的Pool
  • 初始化Pool时, 可以指定一个最大进程数, 当有新的请求提交到Pool中时,如果池还没有满, 那么就会创建一个新的进程用来执行该请求;
    但如果池中的进程数已经达到指定的最大值, 那么该请求就会等待, 直到池中有进程结束, 才会创建新的进程来执行

进程池:

from multiprocessing import Pool
import random,time

def work(num):
    print(random.random()*num)
    time.sleep(3)
if __name__ == "__main__":
    po = Pool(3)        #定义一个进程池,最大进程数为3,默认大小为CPU核数
    for i in range(10):
        po.apply_async(work,(i,))      #apply_async选择要调用的目标,每次循环会用空出来的子进程去调用目标
    po.close()       #进程池关闭之后不再接收新的请求
    po.join()         #等待po中所有子进程结束,必须放在close后面

在多进程中:主进程一般用来等待,真正的任务都在子进程中执行
multiprocessing.Pool常用函数解析:
  • apply_async(func[, args[, kwds]]) : 使用非阻塞方式调用func(并行执行,
    堵塞方式必须等待上一个进程退出才能执行下一个进程) , args为传递给func的参数列表, kwds为传递给func的关键字参数列表;
  • apply(func[, args[, kwds]])(了解即可) 使用阻塞方式调用func
  • close(): 关闭Pool, 使其不再接受新的任务;
  • join(): 主进程阻塞, 等待子进程的退出, 必须在close或terminate之后使用;
进程间通信-Queue :
  • 多进程之间,默认是不共享数据的
  • 通过Queue(队列Q)可以实现进程间的数据传递
  • Q本身是一个消息队列
  • 如何添加消息(入队操作):
from multiprocessing import Queue
q = Queue(3)      #初始化一个Queue对象,最多可接受3条消息
q.put(“消息1”)     #添加的消息数据类型不限
q.put("消息2")
q.put("消息3")
print(q.full())
  • 可以使用multiprocessing模块的Queue实现多进程之间的数据传递
  • 初始化Queue()对象时(例如: q=Queue()) , 若括号中没有指定最⼤可接收 的消息数量, 或数量为负值,
    那么就代表可接受的消息数量没有上限
  • Queue.qsize(): 返回当前队列包含的消息数量
  • Queue.empty(): 如果队列为空, 返回True, 反之False
  • Queue.full(): 如果队列满了, 返回True,反之False
  • Queue.get([block[, timeout]]): 获取队列中的一条消息, 然后将其从列队中移除, block默认值为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(单位秒) , 消息列队如果已 经没有空间可写⼊, 此时程序将被阻塞(停在写入状态), 直到从消息列队腾出空间为止;
  • 如果设置了True和timeout, 则会等待timeout秒, 若还没空间, 则抛出"Queue.Full"异常
    如果block值为False, 消息列队如果没有空间可写入, 则会立刻抛出"Queue.Full"异常
Queue.put_nowait(item): 相当Queue.put(item, False);
from multiprocessing import Queue, Process
import time

def write(q):
    for value in ["a","b","c"]:
        print("开始写入:",value)
        q.put(value)
        time.sleep(1)

def read(q):
    while True:
        if not q.empty():
            print("读取到的是",q.get()) 
            time.sleep(1)
        else:
            break
if __name__ == "__main__":
    q = Queue()
    pw = Process(target=write, args=(q,))
    pr = Process(target=read, args=(q,))
    pw.start()
    pw.join()#等待接收完毕
    pr.start()
    pr.join()
    print("接收完毕!")


问题:
如果有两个接收方怎么办?(多任务之间配合)
  • 进程池创建的进程之间通信:如果要使用Pool创建进程,
    就需要使用multiprocessing.Manager()中的Queue()而不是multiprocessing.Queue()
  • 否则会得到一条如下的错误信息:RuntimeError: Queue objects should only be shared between processes through inheritance.
from multiprocessing import Manager,Pool
import time

def writer(q):
    for i in "welcome":
        print("开始写入",i)
        q.put(i)

def reader(q):
    time.sleep(3)
    for i in range(q.qsize()):
        print("得到消息",q.get())
if __name__ == "__main__":
    print("主进程启动")
    q = Manager().Queue()
    po = Pool()
    po.apply_async(writer,(q,))
    po.apply_async(reader,(q,))
    po.close()
    po.join()
posted @ 2020-08-30 13:18  知秋一叶9527  阅读(148)  评论(0编辑  收藏  举报