day030学习笔记

线程同步

  • 概念

    • 多个线程⼏乎同时修改某⼀个共享数据的时候, 需要进⾏同步控制
    • 引入互斥锁以确保每次只有⼀个线程进⾏写⼊操作,从⽽保证了多线程情况下数据的正 确性(原子性)
    • 互斥锁为资源引入一个状态:锁定/非锁定。某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;直到该线程释放资源,将资 源的状态变成“非锁定”,其他的线程才能再次锁定该资源。互斥锁保证了每次只 有一个线程进行写入操作,从而保证了多线程情况下数据的正确性
  • 效果

    • 确保对数据的修改正确
    • 会降低执行效率
  • 互斥锁实现

    img

    • 创建锁 mutex = threading.Lock()
    • 锁定 mutex.acquire()
    • 释放 mutex.release()
    • 理解:线程可以从锁中切出,但是其他线程的锁中内容必须等当前锁完全执行完才能继续获取锁(被阻塞)
    import threading
    import time
    num = 0
    def f1():
        global num
        mutex.acquire()
        print("进入锁",num)
        time.sleep(2)
        for i in range(1000000):
            num += 1
        print("f1操作后",num)
        mutex.release()
    
    def f2():
        global num
        print("f2中",num)
        mutex.acquire() #如果后被调度到此会被阻塞
        for i in range(1000000):
            num += 1
        print("f2操作后",num)
        mutex.release()
        num += 88
    
    
    mutex = threading.Lock()
    
    t1 = threading.Thread(target=f1)
    t2 = threading.Thread(target=f2)
    t1.start()
    t2.start()
    time.sleep(3)
    print(num)
    >>>进入锁 0 #f1先获得锁
    >>>f2中 0 #切到f2中的锁之前
    >>>f1操作后 1000000
    >>>f2操作后 2000000 #等待f1锁中内容执行完后(当前锁的阻塞状态解除),获取锁开始执行
    >>>2000088
    
  • 死锁

    多个线程多个锁互相需要但多个线程抢到了不同的锁(互相等待对方释放锁)

  • 信号量Semaphore

    • 控制一个时间点内线程进入数量的锁,信号量用来控制线程并发数
    • 理解为计数器,仅起到控制线程个数的功能
    import threading
    import time
    
    s1 = threading.Semaphore(2)
    
    def f1():
        s1.acquire()
        print("123")
        time.sleep(1)
        s1.release()
    
    for i in range(10):
        t = threading.Thread(target=f1)
        t.start()
    

GIL全局解释器锁

  • 执行Python文件的过程
    • 操作系统将你的应用程序从硬盘加载到内存。运行python文件,在内存中开辟一个进程空间,将你的 Python解释器以及py文件加载进去,解释器运行py文件。
    • Python解释器分为两部分,先将你的代码通过编译器编译成C的字节码,然后你的虚拟机拿到你的C的字节码,输出机器码,再配合操作系统把你的这个机器码扔给cpu去执行
    • py文件中有一个主线程,主线程做的就是这个过程。如果开多线程,每个线程都要进行这个过程
  • GIL全局解释器锁做了什么
    • Cpython在所有线程进入解释器之前加了一个全局解释器锁(GIL锁)-互斥锁
    • 导致同一时间只有一个线程在执行所以无法使用多核(即不能同时多个线程一起执行)

同步和异步

  • 概念

    • 同步调⽤:确定调用的顺序
      • 提交一个任务,自任务开始运行直到此任务结束,我再提交下一个任务
      • 顺序、统一的时间轴
    • 异步调⽤:不确定顺序
      • 一次提交多个任务,然后我就直接执行下一行代码
      • 乱序、效率优先的时间轴
  • 线程同步

    多个线程有序执⾏(通过互斥锁实现)-有序释放锁;

    import threading,time
    class Task1(threading.Thread):
        def run(self) -> None:
            while True:
                lock1.acquire()
                print("task1")
                time.sleep(1)
                lock2.release()
    
    class Task2(threading.Thread):
        def run(self) -> None:
            while True:
                lock2.acquire()
                print("task2")
                time.sleep(1)
                lock3.release()
    class Task3(threading.Thread):
        def run(self) -> None:
            while True:
                lock3.acquire()
                print("task3")
                time.sleep(1)
                lock1.release()
    
    lock1 = threading.Lock()
    
    
    lock2 = threading.Lock()
    lock2.acquire() #先将两个进程锁住(提前获取锁)
    lock3 = threading.Lock()
    lock3.acquire()
    t1 = Task1()
    t2 = Task2()
    t3 = Task3()
    t1.start()
    t2.start()
    t3.start()
    >>>task1
    >>>task2
    >>>task3
    ……
    
  • ⽣产者消费者模式

    将一块缓冲区放在生产者和消费者之间,两者的数据交互不互相依赖(降低了耦合性)

    • 实现方式-消息队列

      动态设置放与取的区间,防止发生阻塞

      import queue
      import threading
      
      def write():
          while True:
              if q1.qsize() <= 600:
                  for i in range(1,51):
                      q1.put(f"新数据{i}")
      
      def read():
          while True:
              if q1.qsize() >= 100:
                  for i in range(20):
                      print("读取",q1.get())
      
      q1 = queue.Queue()
      for i in range(1,500):
          q1.put(f"初始数据{i}")
      
      t1 = threading.Thread(target=write)
      t2 = threading.Thread(target=read)
      t1.start()
      t2.start()
      
  • 异步实现

    • 同时启动多个线程(批量创建,批量就绪)

    • 进程的回调

      from  multiprocessing import Pool,Process
      import random
      
      def download(name):
          import time
          for i in range(1,4):
              print(f"{name}下载文件{i}")
              time.sleep(random.randint(1,3))
          return f"{name}下载完成"
      
      def alterUser(msg):
          print(msg)
      
      if __name__ == "__main__":
          p = Pool(3)
          # 当func执行完毕后,return的东西会给到回调函数callback 
          p.apply_async(func=download,args=("进程1",),callback = alterUser)
          p.apply_async(func=download,args=("进程2",),callback = alterUser)
          p.apply_async(func=download,args=("进程3",),callback = alterUser)
          p.close()
          p.join()
      

协程

  • 概念

    • ⽐线程更⼩的执⾏单元(微线程)
    • ⼀个线程作为⼀个容器⾥⾯可以放置多个协程(理解为协助实现多线程)
    • 只切换函数调用即可完成多线程,可以减少CPU的切换
    • 协程⾃⼰主动让出CPU
  • 协程实现

    • greenlet 模块(需要安装)

      • 特性:手动设置切换位置
      from greenlet import greenlet
      def t1():
          while True:
              # sum = 0
              print(sum([i for i in range(1,101)]))
              gr2.switch()
              a = input("输入1:")
              print(a)
      def t2():
          while True:
              # sum = 0
              print(sum([i for i in range(1,101)]))
              gr1.switch()
              a = input("输入2:")
              print(a)
      
      gr1 = greenlet(t1)
      gr2 = greenlet(t2)
      gr1.switch()
      
    • gevent 模块

      • 特性:⾃动切换任务(操作系统控制)
      • 遇到IO(指的是input output 输⼊输出)操作时(模块能识别的), ⽐如访问⽹络, 就⾃动切换到其他的greenlet, 等到IO操作完成, 再在适当的时候切换回来继续执⾏
      • 如果所有程序都没有IO操作,那么就基本属于串行执行了
      import gevent
      def t1():
          while True:
              # sum = 0
              print(sum([i for i in range(1,101)]))
              gevent.sleep(1)#用来模拟一个耗时操作 
              #gevent中:当一个协程遇到耗时操作会自动交出控制权给其他协程 
              a = input("输入1:")
              print(a)
      def t2():
          while True:
              # sum = 0
              print(sum([i for i in range(1,101)]))
              gevent.sleep(1)
              a = input("输入2:") #每当遇到耗时操作,会自用转到其他协程 
              print(a)
      
      gr1 = gevent.spawn(t1) # 创建一个gevent对象(创建了一个协程),此时就已经开始执行t1
      gr2 = gevent.spawn(t2)
      gr1.switch()
      
posted @ 2019-10-18 08:17  寂静四月  阅读(113)  评论(0)    收藏  举报