zoukankan      html  css  js  c++  java
  • 并发编程笔记(1)——多进程、同步/异步/阻塞/非阻塞、守护进程、进程锁

    内容目录

    • 操作系统的历史
    • 进程

    内容详细:

    操作系统:

    • 一:隐藏了丑陋的硬件调用接口,为应用程序员提供调用硬件资源的更好,更简单,更清晰的模型(系统调用接口)。应用程序员有了这些接口后,就不用再考虑操作硬件的细节,专心开发自己的应用程序即可。
      例如:操作系统提供了文件这个抽象概念,对文件的操作就是对磁盘的操作,有了文件我们无需再去考虑关于磁盘的读写控制(比如控制磁盘转动,移动磁头读写数据等细节),

    • 二:将应用程序对硬件资源的竞态请求变得有序化
      例如:很多应用软件其实是共享一套计算机硬件,比方说有可能有三个应用程序同时需要申请打印机来输出内容,那么a程序竞争到了打印机资源就打印,然后可能是b竞争到打印机资源,也可能是c,这就导致了无序,打印机可能打印一段a的内容然后又去打印c...,操作系统的一个功能就是将这种无序变得有序。

    进程:

    • 隐藏丑陋复杂的硬件接口,提供良好的抽象接口
    • 管理、调度进程,并且将多个进程对硬件的竞争变得有序

    同步/异步/阻塞/非阻塞

    多进程模块:multiprocessing

    from multiprocessing import Process #多进程必用模块
    def func():
        print(12345)
    
    if __name__ == '__main__':
        p = Process(target=func)    # 把一个函数注册进这个进程里面
        # p是一个进程对象,还没有启动进程
        p.start()   #进程启动
        print('*'*10)
    
    #多进程:带参数的函数
    from multiprocessing import Process
    def func(args):
        print(args)
    
    if __name__ == '__main__':
        p = Process(target=func,args=('参数',))  #传参时必须是元组
        p.start()   #进程启动
    

    os.pid:获取当前进程号

    import os
    from multiprocessing import Process #多进程必用模块
    def func():
        print(12345)
        print('子进程:',os.getpid())
        print('子进程的父进程:',os.getppid())
    
    if __name__ == '__main__':
        p = Process(target=func)    # 把一个函数注册进这个进程里面
        # p是一个进程对象,还没有启动进程
        p.start()   #进程启动
        print('*'*10)
        print('主进程:',os.getpid())
        print('主进程的父进程:',os.getppid())
    
    • 当前程序为主进程,如果新建了进程,并把函数注册进了该进程,则该进程与主进程同时运行。
    • 主进程的父进程为pycharm

    进程的生命周期

    • 主进程
    • 子进程
    • 开启了子进程的主进程:
      • 主进程的代码如果长,等待主进程自己的代码执行结束
      • 子进程的执行时间长,主进程会在主进程代码执行完毕后等待子进程执行完毕之后,主进程才结束

    进程中join的用法

    • 感知一个子程序的结束,将异步程序改为同步程序

      import time
      from multiprocessing import Process
      def func(arg1,arg2):
          print('*' * arg1)
          time.sleep(5)
          print('*' * arg2)
      
      
      if __name__ == '__main__':
          p = Process(target=func,args=(5,10))#传参时必须是元组
          p.start()   #进程启动
          p.join()    #是感知一个子程序的结束,将异步程序改为同步
          print('=========:执行结束')
      
    • 多个子进程执行完后,主程序再立即执行,需要用到join方法

      from multiprocessing import Process
      
      def func(arg1,arg2):
          print('*'*arg1)
          print('='*arg2)
      
      if __name__ == '__main__':
          p_list = []
          for i in range(10):
              p = Process(target=func,args=(10*i,20*i))
              p_list.append(p)
              p.start()
          [p.join() for p in p_list]  #p.join()相当于把子程序都执行完了,再运行主程序
          #子程序由异步变成同步
          print('主程序执行完毕')
      
    • 应用场景:写文件:同时开启500个进程写文件,异步执行,写文件执行完毕后打印目录

      • 注意,写文件执行完毕后需要用到join方法变为同步,然后再执行打印目录命令
      import os
      from multiprocessing import Process
      
      def func(filename,content):
          with open(filename,'w')as f:
              f.write(content*10*'*')
      
      if __name__ == '__main__':
          p_list = []
          for i in range(1,5):
              p = Process(target=func,args=('info%s'%i,i))
              p_list.append(p)
              p.start()
          [p.join() for p in p_list]  #之前所有进程必须都执行完才能执行下面的代码
          print([i for i in os.walk(r'D:pycharm_projectpython36')])
      

    多个子程序运行

    • 启动多个子程序调用函数,同时执行

      import time
      from multiprocessing import Process
      
      def func(arg1,arg2):
          print('*'*arg1)
          time.sleep(5)
          print('*'*arg2)
      
      if __name__ == '__main__':          #启用多个子程序同时运行
          p1 = Process(target=func,args=(3,10))            #传参
          p1.start()                  #启动子进程
          p2 = Process(target=func,args=(4,11))
          p2.start()
          p3 = Process(target=func,args=(5,12))
          p3.start()
      
    • 也可以使用for循环的方法

      import time
      from multiprocessing import Process
      
      def func(arg1,arg2):
          print('*'*arg1)
          time.sleep(5)
          print('*'*arg2)
      
      if __name__ == '__main__':
          for i in range(4):			#使用for循环该进程,相当于开启了4个子进程
              p1 = Process(target=func,args=(3,10))
              p1.start()
      

    继承类的多线程方法

    • 自定义类,必须继承Process类

    • 必须实现一个run方法,run方法中是在子进程中执行的代码

    • start方法自动处理run方法

      import os
      from multiprocessing import Process
      
      class MyProcess(Process):
          def run(self):
              print(os.getpid())        #要执行的子进程方法
      
      if __name__ == '__main__':
          print('主:',os.getpid())
          p1 = MyProcess()        #实例化类
          p1.start()              #start方法自动处理run方法
          p2 = MyProcess()        #实例化类
          p2.start()              #启动第二个子进程
      
      # 自定义类,必须继承Process类
      # 必须实现一个run方法,run方法中是在子进程中执行的代码
      
    • 传参的方式

      import os
      from multiprocessing import Process
      
      class MyProcess(Process):
          def __init__(self,arg1,arg2):
              super().__init__()      
              self.arg1 = arg1
              self.arg2 = arg2
      
      
          def run(self):
              print(self.pid)
              print(self.name)
              print(self.arg1)
              print(self.arg2)
      
      if __name__ == '__main__':
          print('主:',os.getpid())
          p1 = MyProcess(1,2)        #实例化类
          p1.start()              #启动该子进程
          p2 = MyProcess(3,4)        #实例化类
          p2.start()              #启动第二个子进程
      

    多进程之间的数据隔离问题

    • 子进程中的数据无法修改主进程中的数据

      import os
      from multiprocessing import Process
      
      def func():
          global n        #声明了一个全局变量
          n = 0           #重新定义一个全局变量的n
          print('子进程pid:%s'%os.getpid(),n)
      
      if __name__ == '__main__':  #windows系统下必须使用main
          n = 100
          p = Process(target=func)
          p.start()
          p.join()
          print('主进程pid:%s'%os.getpid(),n)	
      
      #结果为:
      子进程pid:432 0			#在子进程中声明了修改一个全局变量,但是主进程的变量并没有发生改变
      主进程pid:6696 100
      

    socket多进程示例:

    • 开启多个client端,实现多并发

    • 待优化:多进程会增加服务器内存损耗

      # server:
      import socket
      from multiprocessing import Process
      
      def serve(conn):
          ret = '你好'.encode('utf-8')
          conn.send(ret)
          msg = conn.recv(1024).decode('utf-8')
          print(msg)
          conn.close()
      
      if __name__ == '__main__':
          sk = socket.socket()
          sk.bind(('127.0.0.1',8080))
          sk.listen()
          while True:
              conn,addr = sk.accept()
              p = Process(target=serve,args=(conn,))
              p.start()
          sk.close()
          
      # client:
      import socket
      
      sk = socket.socket()
      sk.connect(('127.0.0.1',8080))
      msg = sk.recv(1024).decode('utf-8')
      print(msg)
      msg2 = input('>>>').encode('utf-8')
      sk.send(msg2)
      sk.close()
      

    守护进程

    • 守护进程会随着主进程的代码执行完毕而结束,而不是主进程结束

    • 一定是在子进程start之前设置

      import time
      from multiprocessing import Process
      
      def func():
          while True:
              time.sleep(0.2)
              print('我还活着')
      
      def func2():
          print('in func2 start')
          time.sleep(8)
          print('in func2 finished')
      
      if __name__ == '__main__':
          p = Process(target=func)
          p.daemon = True         #设置该子进程为守护进程
          p.start()
          p2 = Process(target=func2)
          p2.start()
          i = 0
          while i < 5:
              print('我是socket server')
              time.sleep(1)
              i+=1
      # func随着主进程的代码执行结束而结束,而func2子进程继续执行,执行完毕后,主进程整体结束并关闭
      

    强制结束子进程

    • 在主进程内结束一个子进程p.terminate()

      • 结束一个进程不会立即生效,需要操作系统响应的过程
      import time
      from multiprocessing import Process
      
      def func():
          while True:
              time.sleep(0.2)
              print('我还活着')
      
      def func2():
          print('in func2 start')
          time.sleep(5)
          print('in func2 finished')
      
      if __name__ == '__main__':
          p = Process(target=func)
          p.daemon = True         #设置该子进程为守护进程
          p.start()
          p2 = Process(target=func2)
          p2.start()
          p2.terminate()          #结束一个子进程
          print(p2.is_alive())    #立即检验子进程存活状态,此时子进程活着,因为执行后需要操作系统响应的过程
          time.sleep(1)
          print(p2.is_alive())    #检验一个进程是否还活着(True/Flase)
          print(p.name)           #输出子进程的名字
      

    判断子进程是否存活:

    • p.is_alive() -------进程中的方法(True / Flase)
    • p.name -------输出子进程的名字

    进程锁(重点)

    • 必须保证数据安全,所以异步变为同步执行修改数据的代码

      • 关键代码加锁
      • 进程1来了后先拿钥匙进入代码,后续进程为阻塞状态
      • 进程1执行关键代码,执行完毕后把钥匙归还
      • 此时进程2可以拿到钥匙了,进入关键代码,后续进程为阻塞状态
      • 依次循环,直到最后一个进程结束。

    • 进程锁应用:显示余票和买票

      #ticket文件代码:
      {"ticket": 1}
      
      #主程序代码:
      import json
      import time
      from multiprocessing import Process
      from multiprocessing import Lock        #启用进程锁
      
      def show(i):
          with open('ticket')as f:
              dic = json.load(f)
              time.sleep(0.1)         #模拟拿数据时的网络延迟
          print('余票:%s'%dic['ticket'])
      
      def buy_ticket(i,lock):
          lock.acquire()  #拿钥匙进门,只能拿钥匙的进程进去,其他进程为阻塞状态,直到还钥匙为止
          with open('ticket')as f:
              dic = json.load(f)
              time.sleep(0.2)
          if dic['ticket'] > 0:
              dic['ticket'] -= 1
              print('33[32m%s买到票了33[0m'%i)
          else:
              print('33[31m%s没票了33[0m'%i)
          time.sleep(0.1)
          with open('ticket','w') as f:
              json.dump(dic,f)
          lock.release()  #还进程锁的钥匙,此时下一个阻塞进程进场
      
      if __name__ == '__main__':
          for i in range(10):
              p = Process(target=show,args=(i,))
              p.start()
          lock = Lock()           #拿到锁对象
          for i in range(10):
              p = Process(target=buy_ticket,args=(i,lock))
              p.start()
      
  • 相关阅读:
    集群项目总结和思考
    centos7安装zabbix
    string方法中字符与Unicode编码的相互转化:fromCharCode(),charCodeAt()
    element-ui @change添加自定义参数
    js ES6字符串的新功能
    js 去除字符串中的空格
    五月份,是金色的
    夏天快要到来了,2019年还有一半多的时间
    自定义element-ui中的图标
    js下拉框:从数组中筛选出匹配的数据
  • 原文地址:https://www.cnblogs.com/lynlearnde/p/13471723.html
Copyright © 2011-2022 走看看