• Python


    创建进程

    import os
    from multiprocessing import Process
    
    
    def func(args):
        # 要在新进程中执行的函数
        print("子进程1:"+args)
        print("子进程1id:" + str(os.getpid()))
    
    class Myprocess(Process):
        def __init__(self,args):
            # 父类中也有init方法,重载需要调用父类的init方法
            super().__init__()
            # 初始化父类
            self.args = args
            # 传参
    
        def run(self):
            # run方法实现进程要执行的函数
            print("子进程2:"+str(self.args))
            print("子进程2id:" + str(self.pid))
    
    if __name__ == '__main__':
        # 两种方法
    
        # 方法1
        p1 = Process(target=func, args=("参数1",))
        # args的参数为元组类型
    
        # 方法2
        p2 = Myprocess("参数2")
        # 创建进程
        # args的参数是元组类型
    
        print("父进程:****************")
        print("父进程id:" + str(os.getpid()))
    
        # 启动进程
        p1.start()
        p2.start()
    
    
    
    
    

    结束进程

    from multiprocessing import Process
    import time
    def func():
        for i in range(10):
            print(10-i)
            time.sleep(1)
    if __name__ == '__main__':
        a = Process(target=func)
        a.start()
        print(a.is_alive())
        time.sleep(3)
        a.terminate() #发送结束进程的请求
        time.sleep(0.1) #等待系统响应
        print(a.is_alive())# 查看是否存活
    

    join方法

    作用:阻塞当前进程,直到调用join方法的那个进程执行完,再继续执行当前进程。
    感知一个进程的结束

    from multiprocessing import Process
    import time
    
    def func(args):
        print("子进程" + args + "启动")
        time.sleep(2)
        print("子进程" + args + "结束")
    if __name__ == '__main__':
        p = Process(target=func,args=("1"))
        p.start()
    
        p.join()
        # 等待子进程结束,结束后继续执行下面的代码
        print("运行完了")
    

    多进程之间的数据隔离

    from multiprocessing import Process
    
    def func():
        global a
        # 声明a是全局变量
        a = 10
        # 修改a的值
        print("子进程a:"+str(a))
    
    
    
    if __name__ == '__main__':
        a = 1
        p = Process(target=func)
        p.start()
        p.join()
        # 等待子进程结束
        print("父进程a:"+str(a))
        # 输出父进程的a的值
        # a的值并没有因为func函数的修改而改变,这就说明了多进程之间的数据是相互隔离的
    

    守护进程

    守护进程:守护进程会随着主进程的代码执行完毕而结束
    重点在于主进程的代码执行完毕,也就是说如果还有其他进程在执行,主进程不会结束,但是守护进程会结束

    from multiprocessing import Process
    import time
    def func1():
        while True:
            time.sleep(0.5)
            print("守护进程存活")
    def func2():
        print("进程2启动")
        time.sleep(8)
        print("进程2结束")
    if __name__ == '__main__':
        p1 = Process(target=func1)
        p1.daemon = True
        p1.start()
        p2 = Process(target=func2)
        p2.start()
        for i in range(5):
            time.sleep(1)
            print("主进程存活")
        print("主进程代码执行完毕")
    

    进程同步控制

    进程锁

    在日常生活中我们外出时一般都会遇到买票的情况,现在网络发达,大部分人都会选择在网上买票,而进程锁在这种情况下就会有很大作用

    无进程锁情况下的买票程序

    存有票数量的文件,json格式

    {"ticket": 1}
    

    源代码

    import json
    from multiprocessing import Process
    import time
    
    
    
    def buy_ticket():
        with open("ticket") as f:
            dic = json.load(f)
        if dic["ticket"] > 0:
            time.sleep(0.1) # 模拟网络延迟
            print("买到票了")
            dic["ticket"] -= 1
        else:
            print("没买到票")
        with open("ticket","w") as f:
            json.dump(dic,f)
    
    if __name__ == '__main__':
    
        for i in range(10):
            Process(target=buy_ticket).start()
    
    

    结果,每次运行结果都可能不同

    买到票了
    买到票了
    没买到票
    买到票了
    买到票了
    买到票了
    买到票了
    没买到票
    没买到票
    没买到票
    

    总共只有一张票,程序却卖出了六张
    这肯定不符合我们的要求
    要实现有一张票就只能卖给一个人那就要用进程锁来实现

    有进程锁的买票

    进程锁就是相当于有一个房子,房子只有一个门,门上面的锁就只有一个钥匙,第一个来的进程可以拿到钥匙,并带着钥匙进入房间,完成任务后将钥匙归还,下一进程才能得到钥匙进入房间,这就避免了同时又多个进程同时执行而得到错误的结果

    票数文件

    {"ticket": 1}
    

    源代码

    import json
    from multiprocessing import Process
    import time
    from multiprocessing import Lock
    
    
    
    def buy_ticket(lock):
        lock.acquire() # 拿钥匙 进房子
        with open("ticket") as f:
            dic = json.load(f)
        if dic["ticket"] > 0:
            time.sleep(0.1) # 模拟网络延迟
            print("买到票了")
            dic["ticket"] -= 1
        else:
            print("没买到票")
        with open("ticket","w") as f:
            json.dump(dic,f)
        lock.release() # 出房子 还钥匙
    
    
    if __name__ == '__main__':
        lock = Lock()
        for i in range(10):
            Process(target=buy_ticket,args=(lock,)).start()
    

    信号量

    假设一间房子只有4个座位,在房子门口放了4把钥匙,每次来人都会带着一把钥匙进房子,完成任务出门时会把钥匙放回门口,如果门口没有钥匙了就得等待房子里面的人完成任务出来,然后拿他归还的钥匙进入

    from multiprocessing import Process
    from multiprocessing import Semaphore
    import time
    import random
    def func(s,sem):
        sem.acquire() # 拿钥匙 进房子
        print("%s进入房间"% s)
        time.sleep(random.randint(1,5))# 每个人进入房间的人随机在房间内待1-5秒
        print("%s走出房间"% s)
        sem.release()# 出房子 还钥匙
    if __name__ == '__main__':
        sem = Semaphore(4)# 设置房间内只有4个座位
        for i in range(10):
            Process(target=func,args=(i,sem)).start()
    
    

    事件

    事件可以统一控制进程的阻塞和通行
    红绿灯程序

    from multiprocessing import Process,Event
    import time
    import random
    
    def car(e,i):
        if not e.is_set():
            # 如果为红灯则等待
            print("车%s正在等待" % i)
            e.wait() # 等待e.is_set == True,否则一直等待
        print("车%s通过" % i)# 变为绿灯后直接通过
    
    def light(e):
        while True:
            if e.is_set():
                # 如果为绿灯则变为红灯
                e.clear()# 将e.is_set设置为False
                print("红灯亮")
            else:
                # 如果红灯则变为绿灯
                e.set()# 将e.is_set设置为True
                print("绿灯亮")
            time.sleep(2)# 两秒切换一次红绿灯
    
    if __name__ == '__main__':
        e = Event()# 创建事件
    
        Process(target=light,args=(e,)).start()
        # 启动红绿灯进程
        for i in range(20):
            # 生成二十个车的进程
            Process(target=car,args=(e,i)).start()
            # 启动进程
            time.sleep(random.random())
            # 随机等待0-1秒
    

    进程通信

    queue

    from multiprocessing import Queue,Process
    
    def f1(q):
        q.put("hello")
    
    def f2(q):
        print(q.get())
    
    if __name__ == '__main__':
        q = Queue()
        # 可以填写整数型参数,用来确定队列的长度
        Process(target=f1,args=(q,)).start()
        Process(target=f2,args=(q,)).start()
    

    消费者生产者模型

    queue实现

    from multiprocessing import Queue,Process
    import time
    import random
    def procuder(name,food,q):
        for i in range(10):
            time.sleep(random.randint(1,3))# 模拟生产所消耗的时间
            s = "%s生产了%s%s"%(name,food,i)
            print(s)
            q.put(s)
    
    
    def consumer(name,q):
        while True:
            time.sleep(random.randint(1,3))# 模拟消费所消耗的时间
            f = q.get()
            if f is None:break # 判断队列是否已经没有需要处理的数据
            print("%s消耗了%s"%(name,f))
    
    if __name__ == '__main__':
        q = Queue()
        p1 = Process(target=procuder,args=("A","包子",q))
        p1.start()
        p2 = Process(target=procuder,args=("B","面条",q))
        p2.start()
    
        Process(target=consumer,args=("C",q)).start()
        Process(target=consumer,args=("D",q)).start()
    
        p1.join()
        p2.join()
        # 两个生产者进程结束表明已经没有需要处理的数据
        q.put(None)
        q.put(None)
        # 队列的数据只能被一个进程获取
        # 要保证每一个消费者进程都结束
        # 所以每有一个消费者进程就要向队列中添加一个None
    

    joinablequeue 实现

    使用 queue需要给每个进程添加一个结束标志
    使用joinablequeue可以避免这个问题

    from multiprocessing import JoinableQueue,Process
    import time
    import random
    def procuder(name,food,q):
        for i in range(10):
            time.sleep(random.randint(1,3))# 模拟生产所消耗的时间
            s = "%s生产了%s%s"%(name,food,i)
            print(s)
            q.put(s)
        q.join()
        # 生产已经结束
        # 阻塞,直到感知到消费者把所有数据都处理完
        # 也就是感知所有的记号都被清除后解除阻塞
    
    
    def consumer(name,q):
        while True:
            time.sleep(random.randint(1,3))# 模拟消费所消耗的时间
            f = q.get()
            print("%s消耗了%s"%(name,f))
            q.task_done()#抹除记号
    
    if __name__ == '__main__':
        q = JoinableQueue()
    
        p1 = Process(target=procuder,args=("A","包子",q))
        p2 = Process(target=procuder,args=("B","面条",q))
        p1.start()
        p2.start()
    
        c1 = Process(target=consumer,args=("C",q))
        c2 = Process(target=consumer,args=("D",q))
    
        # 设置守护进程
        c1.daemon = True
        c2.daemon = True
        c1.start()
        c2.start()
    
        # 感知生产者函数的结束
        p1.join()
        p2.join()
    
        '''
        joinablequeue 每次put都会留下一个记号
        每次get需要手动抹除一个记号
        
        生产者的join方法保证了所有数据都被处理完了才会结束生产者进程
        主函数中的生产者进程的join保证了生产者进程结束后主函数代码才执行完毕
        消费者进程设置为守护进程会随着主函数代码执行完毕而结束
        
        '''
    

    管道

    from multiprocessing import Pipe,Process
    def func(conn):
        print(conn.recv())#接收从管道端口2发送到端口1的数据
    
    if __name__ == '__main__':
        conn1,conn2 = Pipe() # 创建管道
        Process(target=func,args=(conn1,)).start()# 创建进程并将管道端口1传入
        conn2.send("123456")# 从管道端口2传入数据
        # 管道是双向通信的组件
    

    进程池

    map

    • 异步,自带close和join方法
    • 返回值是所有结果的列表
    • map会等待所有结果都计算完成后一块返回
    from multiprocessing import Pool,Process
    import time
    
    
    def func(n):
        for i in range(10):
            print(n+1)
    
    
    if __name__ == '__main__':
        p = Pool(5)
        # 参数可以为空,为空默认为cpu数量或1
        # 创建一个进程池,里面有5个进程
        start = time.time()
        # 启动时间
        p.map(func,range(100))
        # p.map(方法,参数(可迭代))
        # 创建100个任务
        t1 = time.time() - start
    
        start = time.time()
        p_list = []
        for i in range(100):
            p1 = Process(target=func, args=(i,))
            p_list.append(p1)
            p1.start()
        for i in p_list:i.join()
        t2 = time.time() - start
        print("进程池:"+str(t1), "普通进程:"+str(t2))
    
    
    
    

    apply

    • 同步:只有func执行完成后才会向后执行
    • 返回值就是func的return
    from multiprocessing import Pool
    import os
    import time
    
    
    def func(n):
        print("start:%s" % n, os.getpid())
        time.sleep(1)
        print("end:%s" % n, os.getpid())
    
    
    if __name__ == '__main__':
        p = Pool()
        for i in range(10):
            p.apply(func=func,args=(i,))
        p.close()
        # 进程池停止接收任务
        p.join()
        # 感知进程池任务结束
    

    apply_async

    • 异步
    from multiprocessing import Pool
    import os
    import time
    
    
    def func(n):
        print("start:%s" % n, os.getpid())
        time.sleep(1)
        print("end:%s" % n, os.getpid())
    
    
    if __name__ == '__main__':
        p = Pool()
        for i in range(10):
            p.apply_async(func=func,args=(i,))
        p.close()
        # 进程池停止接收任务
        p.join()
        # 感知进程池任务结束
    

    进程池的返回值

    from multiprocessing import Pool
    import time
    
    
    def func(n):
        time.sleep(0.5)
        return n*n
    
    
    if __name__ == '__main__':
        p = Pool(5)
    
        print("map:")
        # map的返回值是等待所有结果执行完毕统一返回
        ret = p.map(func,range(10))
        print(ret)
    
        print("apply_async:")
        # apply_async的返回值是进程池内的一批任务执行完毕就返回一批的结果
        ret_lst = []
        for i in range(10):
            ret = p.apply_async(func,args=(i,))
            # 创建任务
            ret_lst.append(ret)
            # 将任务的结果存入结果列表
        for i in ret_lst:print(i.get())
        # 打印结果
    
        # 如果apply_async之后直接接ret.get()则会将进程池转换为同步进程池
        # 因为ret.get会阻塞进程等待返回值,这样就相当于把进程池变成了同步进程池
    
    

    Windows中需要注意的点

    错误1

    当出现这种错误的时候说明你的进程调用没有判断是否在__name__ == __main__的环境下运行

    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "C:py3.7libmultiprocessingspawn.py", line 105, in spawn_main
        exitcode = _main(fd)
      File "C:py3.7libmultiprocessingspawn.py", line 114, in _main
        prepare(preparation_data)
      File "C:py3.7libmultiprocessingspawn.py", line 225, in prepare
        _fixup_main_from_path(data['init_main_from_path'])
      File "C:py3.7libmultiprocessingspawn.py", line 277, in _fixup_main_from_path
        run_name="__mp_main__")
      File "C:py3.7lib
    unpy.py", line 263, in run_path
        pkg_name=pkg_name, script_name=fname)
      File "C:py3.7lib
    unpy.py", line 96, in _run_module_code
        mod_name, mod_spec, pkg_name, script_name)
      File "C:py3.7lib
    unpy.py", line 85, in _run_code
        exec(code, run_globals)
      File "F:编程语言区python源码学习进程控制多进程杀死进程.py", line 10, in <module>
        a.start()
      File "C:py3.7libmultiprocessingprocess.py", line 112, in start
        self._popen = self._Popen(self)
      File "C:py3.7libmultiprocessingcontext.py", line 223, in _Popen
        return _default_context.get_context().Process._Popen(process_obj)
      File "C:py3.7libmultiprocessingcontext.py", line 322, in _Popen
        return Popen(process_obj)
      File "C:py3.7libmultiprocessingpopen_spawn_win32.py", line 33, in __init__
        prep_data = spawn.get_preparation_data(process_obj._name)
      File "C:py3.7libmultiprocessingspawn.py", line 143, in get_preparation_data
        _check_not_importing_main()
      File "C:py3.7libmultiprocessingspawn.py", line 136, in _check_not_importing_main
        is not going to be frozen to produce an executable.''')
    RuntimeError: 
            An attempt has been made to start a new process before the
            current process has finished its bootstrapping phase.
    
            This probably means that you are not using fork to start your
            child processes and you have forgotten to use the proper idiom
            in the main module:
    
                if __name__ == '__main__':
                    freeze_support()
                    ...
    
            The "freeze_support()" line can be omitted if the program
            is not going to be frozen to produce an executable.
    
    

    解决方法:
    将进程的调用放到如下代码中执行即可

    if __name__ == '__main__':
        a = Process(target=func)
        a.start()
    
  • 相关阅读:
    C语言--存储类、链接和内存管理
    Linux终端使用技巧——个人总结
    mini2440应用例程学习(二)—— buttons
    ubuntu安装配置NFS服务方便mini2440挂载
    shell中常用I/O重定向命令格式说明
    Linux Bash内置命令大全详细介绍
    mini2440应用例程学习(一)—— led-player
    Shell编程练习(一)——ping一下
    < IOS开发 >使用CGContextRef绘制文字时的设置
    < Objective-C >使用kvc获取数组最大最小值
  • 原文地址:https://www.cnblogs.com/changjiangwei/p/11517542.html
Copyright © 2020-2023  润新知