并发编程-多进程

并发编程-多进程

1.并发编程

​ 并发指的是多个任务同时被执行,并发编程指的是编写支持多任务并发的应用程序。

2. 进程

进程指的是正在运行的程序,是一系列过程的统称,也是操作系统在调度和进行资源分配的基本单位.进程是实现并发的一种方式.

3. 进程与程序

​ 进程是正在运行的程序,程序是程序员编写的一堆代码,也就是一堆字符,当这堆代码被系统加载到内存中并执行时,就有了进程。

需要注意的是:一个程序是可以产生多个进程的,就像我们可以同时运行多个QQ程序一样,会形成多个进程

4.PID和PPID

​ PID:系统会给每一个进程分配一个进程编号即PID,如同人需要一个身份证号来区分。

验证:

​ 1.tasklist 用于查看所有的进程信息

​ 2.taskkill /f /pid pid 该命令可以用于结束指定进程

​ PPID:当一个进程a开启了另一个进程b时,a称为b的父进程,b称为a的子进程

在python中可以通过os模块来获取父进程的pid.

​ 如果是在pycharm中运行的py文件,那pycahrm就是这个python.exe的父进程,当然你可以从cmd中来运行py文件,那此时cmd就是python.exe的父进程

5.并发与并行,阻塞与非阻塞

并发指的是,多个事件同时发生

例如洗衣服和做饭,同时发生了,但本质上是两个任务在切换,给人的感觉是同时在进行,也被称为伪并行

并行指的是,多个事件同时进行着

例如一个人在写代码另一个人在写书,这两件事件是同时在进行的,要注意的是一个人是无法真正的并行执行任务的,在计算机中单核CPU也是无法真正并行的,之所以单核CPU也能同时运行qq和微信其实就是并发执行

阻塞与非阻塞指的是程序的状态

阻塞状态是因为程序遇到了IO操作,或是sleep,导致后续的代码不能被CPU执行

非阻塞与之相反,表示程序正在正常被CPU执行

补充:进程有三种状态

就绪态,运行态,和阻塞态

img

6.进程相关理论

​ 而对于通用系统(跑很多应用程序),需要有系统运行过程中创建或撤销进程的能力,主要分为4中形式创建新的进程

  1. 系统初始化(查看进程linux中用ps命令,windows中用任务管理器,前台进程负责与用户交互,后台运行的进程与用户无关,运行在后台并且只在需要时才唤醒的进程,称为守护进程,如电子邮件、web页面、新闻、打印)
    2. 一个进程在运行过程中开启了子进程(如nginx开启多进程,os.fork,subprocess.Popen等)
    3. 用户的交互式请求,而创建一个新进程(如用户双击暴风影音)
  2. 一个批处理作业的初始化(只在大型机的批处理系统中应用)

 关于创建的子进程,UNIX和windows

  1.相同的是:进程创建后,父进程和子进程有各自不同的地址空间(多道技术要求物理层面实现进程之间内存的隔离),任何一个进程的在其地址空间中的修改都不会影响到另外一个进程。

  2.不同的是:在UNIX中,子进程的初始地址空间是父进程的一个副本,提示:子进程和父进程是可以有只读的共享内存区的。但是对于windows系统来说,会重新加载程序代码。

进程的销毁

  1. 正常退出(自愿,如用户点击交互式页面的叉号,或程序执行完毕调用发起系统调用正常退出,在linux中用exit,在windows中用ExitProcess)
  2. 出错退出(自愿,python a.py中a.py不存在)
  3. 严重错误(非自愿,执行非法指令,如引用不存在的内存,1/0等,可以捕捉异常,try...except...)
  4. 被其他进程杀死(非自愿,如kill -9)

进程的层次结构

无论UNIX还是windows,进程只有一个父进程,不同的是:

  1. 在UNIX中所有的进程,都是以init进程为根,组成树形结构。父子进程共同组成一个进程组,这样,当从键盘发出一个信号时,该信号被送给当前与键盘相关的进程组中的所有成员。
  2. 在windows中,没有进程层次的概念,所有的进程都是地位相同的,唯一类似于进程层次的暗示,是在创建进程时,父进程得到一个特别的令牌(称为句柄),该句柄可以用来控制子进程,但是父进程有权把该句柄传给其他子进程,这样就没有层次了。

7.Python实现多进程

  1. 实例化Process类
import os

from multiprocessing import Process
import time


def task(name):
    print('%s is running'%name)	#son is running
    print(os.getpid(),os.getppid())	#12692 17576

if __name__ == '__main__':
    # p = Process(target=task,args=('son',))
    p = Process(target=task,kwargs={'name':'son'})

    time.sleep(1)
    p.start()
    print('father process',os.getpid())	#father process,17576

linux 与windows开启进程的方式不同

linux 会将父进程的内存数据 完整copy一份给子进程

注意:

​ windows 会导入父进程的代码 从头执行一遍 来获取需要处理的任务

​ 所以在编写代码时如果是windows一定要将开启进程的代码放main判断中

  1. 继承Process类 并覆盖run方法
from multiprocessing import Process
import os

#自定义进程对象
class Downloader(Process):

    # def __init__(self,url,size,name):
    #     super().__init__()
    #     self.url = url
    #     self.size = size
    #     self.name = name

    def run(self):
        print(os.getpid())
        pass

if __name__ == '__main__':
    m = Downloader()
    m.start()
    print("parent over",os.getpid())

如果需要对进程对象进行高度自定义那就可以继承它

需要注意的是

1.在windows下 开启子进程必须放到__main__下面,因为windows在开启子进程时会重新加载所有的代码造成递归创建进程

2.第二种方式中,必须将要执行的代码放到run方法中,子进程只会执行run方法其他的一概不管

3.start仅仅是给操作系统发送消息,而操作系统创建进程是要花费时间的,所以会有两种情况发送

a.开启进程速度慢于程序执行速度,先打印父进程 在打印task中的消息

b.开启进程速度快于程序执行速度,先打印task中的消息,在打印父进程

进程之间内存相互隔离

from multiprocessing import Process
import time
x=1000
def task():
    global x
    x=0
    print('son',x)


if __name__ == '__main_
    print(x)	#1000
    p=Process(target=task)
    p.start()	#son,0
    time.sleep(5)
    print(x)	#1000

join函数的使用

# join的使用(利用join达到串行的效果)
from multiprocessing import Process
import time
def task1(name):
    for i in range(10):
        print("%s run" % name)


if __name__ == '__main__': # args 是给子进程传递的参数 必须是元组


    ps = []
    for i in range(10):
        p = Process(target=task1,args=(i,))
        p.start()
        ps.append(p)

    # 挨个join以下
    for i in ps:
        i.join()

    print("over")

进程对对象的常用属性

from multiprocessing import Process
def task(n):
    print('%s is running'%n)

if __name__ == '__main__':
    p = Process(target=task,args=('wq',),name = 'liu_jin')
     p.start()       #启动子进程
    print(p.pid)    #18664(获取子进程的pid)
    print(p.name)   #获取子进程名
     p.terminate()   #终止子进程
     p.join()        #提高子进程优先级
    print(p.is_alive()) #获取进程的存活状态(关键在与子进程是(true)否(false)被start)

孤儿进程和僵尸进程

什么是孤儿进程

​ 孤儿进程指的是开启子进程后,父进程先于子进程终止了,那这个子进程就称之为孤儿进程

例如:qq聊天中别人发给你一个链接,点击后打开了浏览器,那qq就是浏览器的父进程,然后退出qq,此时浏览器就成了孤儿进程

孤儿进程是无害的,有其存在的必要性,在父进程结束后,其子进程会被操作系统接管。

什么是僵尸进程

​ 僵尸进程指的是,当子进程比父进程先结束,而父进程又没有回收子进程,释放子进程占用的资源,此时子进程将成为一个僵尸进程。该情况仅在linux下出现。windows中进程间完全是独立的没有任何关联。

如果父进程先退出 ,子进程被操作系统接管,子进程退出后操作系统会回收其占用的相关资源!

原文地址:https://www.cnblogs.com/bruce123/p/11184408.html