• 进程


    进程:正在进行的一个过程或者说一个任务。而负责执行任务则是cpu。

    进程的创建:

    1 fork(在Unix/Linux上运行)

      进程与程序

      编写完毕的代码,在没有运行的时候,称之为程序。在在运行的代码,就成为进程

    #!/urs/bin/evn python
    # -*- coding:utf-8 -*-
    import os
    import time
    """
    res = os.fork()
    if res ==0:
        print("子进程1")
        time.sleep(5)
        print("子进程2")
    else:
        print("父进程1")
        time.sleep(5)
    
    print("----over----")
    """
    
    
    ret = os.fork()
    if ret == 0:
        while True:
            print("-----1----")
            time.sleep(1)
    else:
        while True:
            print("-----2----")
            time.sleep(1)
    View Code

     2在windows中该系统调用是:CreateProcess,CreateProcess既处理进程的创建,也负责把正确的程序装入新进程。

    #! /urs/bin/evn python
    # -*- coding:utf-8 -*-
    from multiprocessing import Process
    import time
    
    方法一
    def task(name):
        print("%s is running" % name)
        time.sleep(3)
        print("%s is dome" % name)
    
    if __name__ == "__main__":
        p = Process(target=task, args=("子进程",))
        # p = Process(target=task, kwargs={"name": "子进程"})
        p.start()  # 只是给操作系统发送了一个信号
        print("我是主进程")
    class MyProcess(Process):
        def __init__(self, name):
            super().__init__()
            self.name = name
    
        def run(self):  # 默认run
            print("%s is running" % self.name)
            time.sleep(3)
            print("%s is dome" % self.name)
    
    if __name__ == "__main__":
        p = MyProcess("子进程")
        p.start()
        print("")

     

    进程的终止

    1. 正常退出(自愿,如用户点击交互式页面的叉号,或程序执行完毕调用发起系统调用正常退出,在linux中用exit,在windows中用ExitProcess)

    2. 出错退出(自愿,python a.py中a.py不存在)

    3. 严重错误(非自愿,执行非法指令,如引用不存在的内存,1/0等,可以捕捉异常,try...except...)

    4. 被其他进程杀死(非自愿,如kill -9)

    进程的状态

    tail -f access.log |grep '404'

    执行程序tail,开启一个子进程,执行程序grep,开启另外一个子进程,两个进程之间基于管道'|'通讯,将tail的结果作为grep的输入。

    进程grep在等待输入(即I/O)时的状态称为阻塞,此时grep命令都无法运行

    其实在两种情况下会导致一个进程在逻辑上不能运行,

    1. 进程挂起是自身原因,遇到I/O阻塞,便要让出CPU让其他进程去执行,这样保证CPU一直在工作

    2. 与进程无关,是操作系统层面,可能会因为一个进程占用时间过多,或者优先级等原因,而调用其他的进程去使用CPU。

    因而一个进程由三种状态

    了解Process对象的join方法:
    在主进程的任务与子进程的任务彼此独立的情况下,主进程的任务先执行完毕后,主进程还需要等待子进程执行完毕,然后统一回收资源
     如果主进程的任务在执行到某一个阶段时,需要等待子进程执行完毕后才能继续执行,就需要有一种机制能够让主进程检测子进程是否运行完毕,在子进程执行完毕后才继续执行,否则一直在原地阻塞,这就是join方法的作用
    
    (等待子进程结束,主进程才结束)
    #! /urs/bin/evn python
    # -*- coding:utf-8 -*-
    from multiprocessing import Process
    import time
    import os
    
    """
    def task():
        print("%s is running, parent id is <%s>" %
              (os.getpid(), os.getppid()))
        time.sleep(3)
        print("%s is running, parent id is <%s>" %
              (os.getpid(), os.getppid()))
    
    if __name__ == "__main__":
        p = Process(target=task)
        p.start()
        p.join()  #
        print("主", os.getpid(), os.getppid())
        print(p.pid)
    
    
    # 子进程结束,父进程才结束(主进程)
    """
    
    
    """
    # join 用法
    def task(name):
        
        print("%s is running" % name)
        time.sleep(3)
        print("%s is doew" % name)
    
    if __name__ == "__main__":
        p1 = Process(target=task, args=("子进程1",))
        p2 = Process(target=task, args=("子进程2",))
        p3 = Process(target=task, args=("子进程3",))
        p1.start()
        p2.start()
        p3.start()
    
        p1.join()
        p2.join()
        p3.join()
        print("主", os.getpid(), os.getppid())
    # 并发执行
    """
    """
    
    
    
    def task(name, n):
    
        print("%s is running" % name)
        time.sleep(n)
        print("%s is doew" % name)
    
    
    if __name__ == "__main__":
        start = time.time()
        p1 = Process(target=task, args=("子进程1", 5))
        p2 = Process(target=task, args=("子进程2", 3))
        p3 = Process(target=task, args=("子进程3", 2))
        p1.start()
        p2.start()
        p3.start()  # start()执行后才,执行join
    
        p1.join()
        p2.join()
        p3.join()
        print("主", (time.time()-start))
        
    
    """
    """
    # 串行执行
    # 等待最长时间
    
    def task(name, n):
    
        print("%s is running" % name)
        time.sleep(n)
        print("%s is doew" % name)
    
    
    if __name__ == "__main__":
        start = time.time()
        p1 = Process(target=task, args=("子进程1", 5))
        p2 = Process(target=task, args=("子进程2", 3))
        p3 = Process(target=task, args=("子进程3", 2))
        p1.start()
        p1.join()
        p2.start()
        p2.join()
        p3.start()
        p3.join()
    
        print("主", (time.time()-start))
    """
    
    
    def task(name, n):
    
        print("%s is running" % name)
        time.sleep(n)
        print("%s is doew" % name)
    
    
    if __name__ == "__main__":
        start = time.time()
        p1 = Process(target=task, args=("子进程1", 5))
        p2 = Process(target=task, args=("子进程2", 3))
        p3 = Process(target=task, args=("子进程3", 2))
        p_l = [p1, p2, p3]
        for p in p_l:
            p.start()
    
        for p in p_l:
            p.join()
    
        print("", (time.time()-start))
    
    # 并发执行简写
    from multiprocessing import Process
    import time
    import random
    
    def task(name):
        print('%s is piaoing' %name)
        time.sleep(random.randrange(1,5))
        print('%s is piao end' %name)
    
    if __name__ == '__main__':
        p1=Process(target=task,args=('egon',),name='子进程1') #可以用关键参数来指定进程名
        p1.start()
    
        print(p1.name,p1.pid,)
    
    进程对象的其他属性:name与pid
    View Code
    Process类常用方法:
    start():启动进程,并调用该子进程中的p.run() run():进程启动时运行的方法,正是它去调用target指定的函数,我们自定义类的类中一定要实现该方法 terminate():强制终止进程p,不会进行任何清理操作,如果p创建了子进程,该子进程就成了僵尸进程,使用该方法需要特别小心这种情况。如果p还保存了一个锁那么也将不会被释放,进而导致死锁 is_alive():如果p仍然运行,返回True join([timeout]):主线程等待p终止(强调:是主线程处于等的状态,而p是处于运行的状态)。timeout是可选的超时时间。
    Process类常用属性:
    pid:当前进程实例的PID值
    name:当前进程实例别名。默认为Process-N,N为从1开始调增的整数
     

     

    守护进程

    主进程创建子进程,然后将该进程设置成守护自己的进程.主要作用:

    守护进程会在主进程代码执行结束后就终止

    守护进程内无法再开启子进程,否则抛出异常:AssertionError: daemonic processes are not allowed to have children

     

    如果我们有两个任务需要并发执行,那么开一个主进程和一个子进程分别去执行,如果子进程的任务在主进程任务结束后就没有存在的必要了,那么该子进程应该在开启前就被设置成守护进程。主进程代码运行结束,守护进程随即终止。

    #! /urs/bin/env python
    # -*- coding:utf-8 -*-
    
    from multiprocessing import Process
    import time
    import random
    
    
    def task(name):
        print("%s is piaoing" % name)
        time.sleep(random.randrange(1, 3))
        print("%s is piao end" % name)
    
    if __name__ == "__main__":
        p = Process(target=task, args=("xie",))
        p.daemon = True  # 一定要在p.start前设置,设置p为守护进程。禁止p创建子进程,并且父进程代码执行结束。p即就终止运行
        p.start()
        print("")  # 只要终端打印出这一行,守护进程p也就跟着结束掉了

    互斥锁

    进程之间数据不共享,但是共享同一套文件系统,所以访问同一个文件,或同一个打印终端,是没有问题的,而共享带来的是竞争,竞争带来的结果就是错乱。

    #! /urs/bin/evn python
    # -*- coding:utf-8 -*-
    """
    from multiprocessing import Process
    import time
    
    
    def task(name):
        print("%s 1" % name)
        time.sleep(1)
        print("%s 2" % name)
        time.sleep(1)
        print("%s 3" % name)
    
    if __name__ == "__main__":
        for i in range(3):
            p = Process(target=task, args=("进程%s" % i,))
            p.start()
    # 没有锁,大家共享资源(迸发)
    """
    
    from multiprocessing import Process, Lock
    import time
    
    
    def task(name, mutex):
        mutex.acquire()  # 加锁
        print("%s 1" % name)
        time.sleep(1)
        print("%s 2" % name)
        time.sleep(1)
        print("%s 3" % name)
        mutex.release()   # 释放锁
    
    if __name__ == "__main__":
        mutex = Lock()    #
        for i in range(3):
            p = Process(target=task, args=("进程%s" % i, mutex))
            p.start()
    
    # 由并发变成了串行,牺牲了运行效率,但避免了竞争

     

    模拟抢票

    配置文件文档:

    {"count": 1}
    from multiprocessing import Process, Lock
    import json
    import time
    
    
    def search(name):
        time.sleep(1)
        dic = json.load(open("db.txt", "r",
                             encoding="utf-8")
                        )
        print("<%s>查看到余票数【%s】" % (name, dic["count"]))
    
    
    def get(name):
        time.sleep(1)
        dic = json.load(open("db.txt", "r",
                             encoding="utf-8")
                        )
        if dic["count"] > 0:
            dic["count"] -= 1
            time.sleep(3)   # 模拟读数据的网络延迟
            json.dump(dic, open("db.txt", "w",
                             encoding="utf-8"))
            print("<%s>购票成功" % name)
    
    
    def task(name, metex):
        search(name)
        metex.acquire()
        get(name)
        metex.release()
    if __name__ == "__main__":
        metex = Lock()
        for i in range(10):  # 想购票的人
            p = Process(target=task, args=("路人%s" % i, metex))
            p.start()
    
    运行结果:
    
    <路人0>查看到余票数【1<路人2>查看到余票数【1<路人3>查看到余票数【1<路人1>查看到余票数【1<路人4>查看到余票数【1<路人5>查看到余票数【1<路人7>查看到余票数【1<路人6>查看到余票数【1<路人8>查看到余票数【1<路人9>查看到余票数【1<路人0>购票成功

    加锁处理:购票行为由并发变成了串行,牺牲了运行效率,但保证了数据安全

    加锁可以保证多个进程修改同一块数据时,同一时间只能有一个任务可以进行修改,即串行地修改,速度是慢了,但牺牲了速度却保证了数据安全。

    可以用文件共享数据实现进程间通信,但问题是:

    1、效率低(共享数据基于文件,而文件是硬盘上的数据)

    2、需要自己加锁处理

    最好找寻一种解决方案能够兼顾:

    1、效率高(多个进程共享一块内存的数据)

    2、帮我们处理好锁问题。

    这就是mutiprocessing模块为我们提供的基于消息的IPC通信机制:队列和管道。

    队列和管道都是将数据存放于内存中,而队列又是基于(管道+锁)实现的,可以让我们从复杂的锁问题中解脱出来,因而队列才是进程间通信的最佳选择。

    我们应该尽量避免使用共享数据,尽可能使用消息传递和队列,避免处理复杂的同步和锁问题,而且在进程数目增多时,往往可以获得更好的可获展性。进程彼此之间互相隔离,要实现进程间通信(IPC),multiprocessing模块支持两种形式:队列和管道,这两种方式都是使用消息传递的。

    #!/urs/bin/evn python
    # -*- coding:utf-8 -*-
    from multiprocessing import Queue
    
    #  可以任何数据类型 不应该放大文件,大视频,使用是内存空间
    q = Queue(3)
    q.put("cc")   # 放数据
    q.put(123)
    q.put(333)
    print(q.full())
    
    print(q.get())  # 取数据
    print(q.get())
    print(q.get())
    print(q.empty())  # 清空数据
    
    
    
    主要方法介绍:
    
    q.put方法用以插入数据到队列中。
    q.get方法可以从队列读取并且删除一个元素。
    
    
    
    参数介绍:
    
    maxsize是队列中允许最大项数,省略则无大小限制。
    但需要明确:
        1、队列内存放的是消息而非大数据
        2、队列占用的是内存空间,因而maxsize即便是无大小限制也受限于内存大小
    View Code

    生产者消费者模型介绍:

    生产者指的是生产数据的任务,消费者指的是处理数据的任务,在并发编程中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个问题于是引入了生产者和消费者模式。

    什么是生产者和消费者模式

    生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。这个阻塞队列就是用来给生产者和消费者解耦的

    #!/urs/bin/evn python
    # -*- coding:utf-8 -*-
    from multiprocessing import Process, Queue
    # 生产者指的是生产数据的任务,消费者指的是处理数据任务
    import time
    
    def Producer(q):
        for i in range(1, 10):
            res = "包子%s" % i
            time.sleep(0.5)
            print("生产者生产了%s" % res)
            q.put(res)
    
    
    def consumer(q):
        while True:
            res = q.get()
            if res is None: break
            time.sleep(1)
            print("消费者吃了%s" % res)
    
    if __name__ == "__main__":
        # 容器(把数据生产到一个容器中)
        q = Queue()
        # 生产者
        p1 = Process(target=Producer, args=(q,))
        p2 = Process(target=Producer, args=(q,))
        p3 = Process(target=Producer, args=(q,))
        # 消费者
        c1 = Process(target=consumer, args=(q,))
        c2 = Process(target=consumer, args=(q,))
        p1.start()
        p2.start()
        p3.start()
        c1.start()
        c2.start()
        p1.join()
        p2.join()
        p3.join()
        q.put(None)
        q.put(None)
        print("")
    
    运行结果:
    生产者生产了包子1
    生产者生产了包子1
    生产者生产了包子1
    生产者生产了包子2
    生产者生产了包子2
    生产者生产了包子2
    生产者生产了包子3
    消费者吃了包子1
    生产者生产了包子3
    消费者吃了包子1
    生产者生产了包子3
    生产者生产了包子4
    生产者生产了包子4
    生产者生产了包子4
    生产者生产了包子5
    消费者吃了包子1
    生产者生产了包子5
    消费者吃了包子2
    生产者生产了包子5
    生产者生产了包子6
    生产者生产了包子6
    生产者生产了包子6
    生产者生产了包子7
    消费者吃了包子2
    生产者生产了包子7
    消费者吃了包子2
    生产者生产了包子7
    生产者生产了包子8
    生产者生产了包子8
    生产者生产了包子8
    生产者生产了包子9
    消费者吃了包子3
    消费者吃了包子3
    生产者生产了包子9
    生产者生产了包子9
    主
    消费者吃了包子3
    消费者吃了包子4
    消费者吃了包子4
    消费者吃了包子4
    消费者吃了包子5
    消费者吃了包子5
    消费者吃了包子5
    消费者吃了包子6
    消费者吃了包子6
    消费者吃了包子6
    消费者吃了包子7
    消费者吃了包子7
    消费者吃了包子7
    消费者吃了包子8
    消费者吃了包子8
    消费者吃了包子8
    消费者吃了包子9
    消费者吃了包子9
    消费者吃了包子9
    View Code

    还有另外一种队列提供了这种机制

    from multiprocessing import Process, JoinableQueue
    import time
    
    
    def Producer(q):
        for i in range(1, 4):
            res = "包子%s" % i
            time.sleep(0.5)
            print("生产者生产了%s" % res)
            q.put(res)
        q.join()  # 等到消费者把自己放入队列中的所有的数据都取走之后,生产者才结束
    
    
    def consumer(q):
        while True:
            res = q.get()
            if res is None: break
            time.sleep(1)
            print("消费者吃了%s" % res)
            q.task_done()  # 发送信号给q.join(),说明已经从队列中取走一个数据并处理完毕了
    
    
    if __name__ == "__main__":
        # 容器(把数据生产到一个容器中)
        q = JoinableQueue()
        # 生产者
        p1 = Process(target=Producer, args=(q,))
        p2 = Process(target=Producer, args=(q,))
        p3 = Process(target=Producer, args=(q,))
        # 消费者
        c1 = Process(target=consumer, args=(q,))
        c2 = Process(target=consumer, args=(q,))
        c1.daemon = True
        c2.daemon = True
        p1.start()
        p2.start()
        p3.start()
        c1.start()
        c2.start()
        p1.join()
        p2.join()
        p3.join()
    
        print("")
    
    
    参数介绍
    
    maxsize是队列中允许最大项数,省略则无大小限制。
    方法介绍
    
    JoinableQueue的实例p除了与Queue对象相同的方法之外还具有:
    q.task_done():使用者使用此方法发出信号,表示q.get()的返回项目已经被处理。如果调用此方法的次数大于从队列中删除项目的数量,将引发ValueError异常
    q.join():生产者调用此方法进行阻塞,直到队列中所有的项目均被处理。阻塞将持续到队列中的每个项目均调用q.task_done()方法为止
    View Code

    生产者消费者模型总结

    1、程序中有两类角色

    一类负责生产数据(生产者)
    一类负责处理数据(消费者)
    

    2、引入生产者消费者模型为了解决的问题是

    平衡生产者与消费者之间的速度差
    程序解开耦合
    

    3、如何实现生产者消费者模型

    生产者<--->队列<--->消费者
    
    
  • 相关阅读:
    iOS.CM5.CM4.CM2
    iOS.Library.Architecture
    iOS.Info.plist
    iOS.ARM-Assembly
    Tools.Png.Compression
    MacDev.GetArchOfLibrary
    iOS.C
    iOS.Notification.Bar.Color
    iOS.-.cxx_destruct
    iOS.UITableView.SectionIndex
  • 原文地址:https://www.cnblogs.com/zqxqx/p/9308912.html
Copyright © 2020-2023  润新知