python并发编程

269 阅读4分钟

同步与异步

  • 用来表达任务的提交方式
  • 同步:提交完任务之后原地等待任务的返回结果 期间不做任何事
  • 异步:提交完任务之后不原地等待任务的返回结果 直接去做其他事 有结果自动通知

阻塞与非阻塞

  • 用来表达任务的执行状态
  • 阻塞:阻塞态 堵车
  • 非阻塞:就绪态 运行态

综合使用

  • 同步阻塞
  • 同步非阻塞
  • 异步阻塞
  • 异步非阻塞(效率最高)

创建进程的多种方式

1.鼠标双击软件图标
2.python代码创建进程
 from multiprocessing import Process
 import time


 def task(name):
     print('task is running',name)
     time.sleep(3)
     print('task is over',name)



在不同的操作系统中创建进程底层原理不一样
    windows
        以导入模块的形式创建进程
    linux/mac
        以拷贝代码的形式创建进程

 if __name__ == '__main__':
     # p1 = Process(target=task, args=('jason',))   位置参数
     p1 = Process(target=task, kwargs={'name':'jason123'})  # 关键字参数
     p1.start()  # 异步 告诉操作系统创建一个新的进程 并在该进程中执行task函数
     # task()  # 同步
     print('主')


from multiprocessing import Process
import time


class MyProcess(Process):
    def __init__(self, name, age):
        super().__init__()
        self.name = name
        self.age = age


    def run(self):
        print('run is running', self.name, self.age)
        time.sleep(3)
        print('run is over', self.name, self.age)


if __name__ == '__main__':
    obj = MyProcess('jason', 123)
    obj.start()
    print('主')
    

进程间数据隔离

  • 同一台计算机上的多个进程数据是严格意义上的物理隔离(默认情况下)

      from multiprocessing import Process
      import time
    
      money = 1000
    
    
      def task():
          global money
          money = 666
          print('子进程的task函数查看money', money)
    
    
      if __name__ == '__main__':
          p1 = Process(target=task)
          p1.start()  # 创建子进程
          time.sleep(3)  # 主进程代码等待3秒
          print(money)  # 主进程代码打印money
          
          
    

进程join方法

from multiprocessing import Process
import time


def task(name, n):
    print('%s is running' % name)
    time.sleep(n)
    print('%s is over' % name)


if __name__ == '__main__':
    p1 = Process(target=task, args=('jason1', 1))
    p2 = Process(target=task, args=('jason2', 2))
    p3 = Process(target=task, args=('jason3', 3))
    # p.start()  # 异步
  主进程代码等待子进程代码运行结束再执行
    # p.join()
    # print('主')
    start_time = time.time()
    p1.start()
    p1.join()
    p2.start()
    p2.join()
    p3.start()
    p3.join()
    # p1.join()
    # p2.join()
    # p3.join()
    print(time.time() - start_time)  # 3秒多
    

IPC机制

  • IPC:进程间通信

  • 消息队列:存储数据的地方 所有人都可以存 也都可以取

      from multiprocessing import Queue
    
    
      q = Queue(3)  # 括号内可以指定存储数据的个数
       往消息队列中存放数据
      q.put(111)
       print(q.full())  # 判断队列是否已满
      q.put(222)
      q.put(333)
       print(q.full())  # 判断队列是否已满
       从消息队列中取出数据
      print(q.get())
      print(q.get())
       print(q.empty())  # 判断队列是否为空
      print(q.get())
       print(q.empty())  # 判断队列是否为空
       print(q.get())
      print(q.get_nowait())
    
    
      full() empty() 在多进程中都不能使用!!!
    
    
    
      from multiprocessing import Process, Queue
    
    
      def product(q):
          q.put('子进程p添加的数据')
    
      def consumer(q):
          print('子进程获取队列中的数据', q.get())
    
    
      if __name__ == '__main__':
          q = Queue()
          # 主进程往队列中添加数据
          
          
    

生产者消费者模型

  • 生产者:负责产生数据的'人'

  • 消费者:负责处理数据的'人'

  • 该模型除了有生产者和消费者之外还必须有消息队列(只要是能够提供数据保存服务和提取服务的理论上都可以)

进程对象的多种方法

1.如何查看进程号
        from multiprocessing import Process, current_process
        current_process()
        current_process().pid  
        import os
        os.getpid()
        os.getppid()
2.终止进程
        p1.terminate()
        ps:计算机操作系统都有对应的命令可以直接杀死进程
3.判断进程是否存活
        p1.is_alive()
4.start()
5.join()

守护进程

  • 守护进程会随着守护的进程结束而立刻结束

      吴勇是张红的守护进程 一旦张红嗝屁了 吴勇立刻嗝屁
    
      from multiprocessing import Process
      import time
    
    
      def task(name):
          print('德邦总管:%s' % name)
          time.sleep(3)
          print('德邦总管:%s' % name)
    
    
      if __name__ == '__main__':
          p1 = Process(target=task, args=('大张红',))
          p1.daemon = True
          p1.start()
          time.sleep(1)
          print('恕瑞玛皇帝:小吴勇嗝屁了')
          
          
    

僵尸进程与孤儿进程

  • 僵尸进程:进程执行完毕后并不会立刻销毁所有的数据 会有一些信息短暂保留下来;比如进程号、进程执行时间、进程消耗功率等给父进程查看;所有的进程都会变成僵尸进程
  • 孤儿进程:子进程正常运行 父进程意外死亡 操作系统针对孤儿进程会派遣福利院管理

多进程数据错乱问题

模拟抢票软件

from multiprocessing import Process
import time
import json
import random
  • 查票

     def search(name):
         with open(r'data.json', 'r', encoding='utf8') as f:
             data = json.load(f)
         print('%s在查票 当前余票为:%s' % (name, data.get('ticket_num')))
    
  • 买票

      def buy(name):
          再次确认票
          with open(r'data.json', 'r', encoding='utf8') as f:
              data = json.load(f)
          模拟网络延迟
          time.sleep(random.randint(1, 3))
          判断是否有票 有就买
          if data.get('ticket_num') > 0:
              data['ticket_num'] -= 1
              with open(r'data.json', 'w', encoding='utf8') as f:
                  json.dump(data, f)
              print('%s买票成功' % name)
          else:
              print('%s很倒霉 没有抢到票' % name)
    
    
      def run(name):
          search(name)
          buy(name)
    
    
      if __name__ == '__main__':
          for i in range(10):
              p = Process(target=run, args=('用户%s'%i, ))
              p.start()
    
  • 多进程操作数据很可能会造成数据错乱>>>:互斥锁

  • 互斥锁:将并发变成串行 牺牲了效率但是保障了数据的安全