Python基础复习(第五天)

并发编程

概念理解

  1. 无论是并行和并发,在用户看起来都是"同时"运行的,不管是进程还是线程,都只是一个任务而已,真正干活的是cpu,cpu来做这些任务,一个cpu同一时刻只能执行一个任务
  2. 并发并不是真正的并行,而是利用多道技术+单个cpu做出并行的效果
  3. 如果想要真正同时运行,只有具备多个cpu才能实现并行
  4. 进程的调度,分配给哪个cpu运行,由操作系统说了算
  5. 内存中同时存入多道(多个)程序,cpu从一个进程快速切换到另外一个,使每个进程各自运行几十或几百毫秒,这样,虽然在某一个瞬间,一个cpu只能执行一个任务,但在1秒内,cpu却可以运行多个进程,这就给人产生了并行的错觉,即伪并发,以此来区分多处理器操作系统的真正硬件并行(多个cpu共享同一个物理内存)

几组比较重要的概念

  同步:在发出一个功能调用时,在没有得到结果之前,该调用就不会返回。
  异步:当一个功能调用发出后,调用者不能立刻得到结果
  
  阻塞:当socket工作在阻塞模式的时候,如果没有数据的情况下调用recv函数,则当前线程就会被挂起,直到有数据为止
  非阻塞:指在不能立刻得到结果之前也会立刻返回,同时该函数不会阻塞当前线程
  
  同步与异步针对的是函数/任务的调用方式:同步就是当一个进程发起一个函数(任务)调用的时候,一直等到函数(任务)完成,而进程继续处于激活状态。而异步情况下是当一个进程发起一个函数(任务)调用的时候,不会等函数返回,而是继续往下执行当,函数返回的时候通过状态、通知、事件等方式通知进程任务完成
  阻塞与非阻塞针对的是进程或线程:阻塞是当请求不能满足的时候就将进程挂起,而非阻塞则不会阻塞当前进程

进程的三种状态

  1.就绪态
  2.运行态
  3.阻塞态

multiprocessing模块

  进程没有任何共享状态,进程修改的数据,改动仅限于该进程内
  应用:
        from multiprocessing import Process
        p = Process(target=func,args=(),kwargs={})
        p.start():启动进程,并调用该子进程中的p.run()
        p.run():进程启动时运行的方法,正式它去调用target指定的函数,我们自定义的类中一定要实现该方法
        p.terminate():强制终止进程p,不会进行任何清理操作,如果p创建了子进程,该子进程就成了僵尸进程,使用该方法需要特别小心这种情况。如果p还保存了一个锁,那么锁也不会被释放,进而导致死锁现象
        p.is_alive():如果p仍然运行,返回True
        p.join():主线程等待p终止(是主线程处于等的状态,而p是处于运行的状态),只能join住start开启的进程,不能join住run开启的进程
        p.daemon:默认值False,如果设为True,代表p为后台运行的守护进程,当p的父进程终止时,p也随着终止
        p.name:进程的名称
        p.pid:进程的pid
        p.exitcode:进程在运行时为None、如果-N,表示被信号N结束
        p.authkey:进程的身份验证,默认是由os.urandom()随机生成的32字符的字符串
  Process的使用要点:
        1.在windows中Process()必须放到if__name__ == '__main__':下
        2.创建进程的使用类的方式:
              class P(Process): # 必须继承Process类
                    def __init__(self,name):
                          super.__init__() # 继承父类ini方法
                          self.name = name
                    def run(self):
                          print(self.name)
        3.进程池:每来一个客户端,都在服务端开启一个进程,如果并发来一个万个客户端,要开启一万个进程吗,你自己尝试着在你自己的机器上开启一万个,10万个进程试一试。解决方法:进程池
        4.p.daemon=True一定要在p.start()前设置

进程同步(锁)

  加锁:由并发变成了串行,牺牲了运行效率,但避免了竞争
  from multiprocessing import Process,Lock
  import os,time
  def work(lock):
        lock.acquire()
        print('%s is running' %os.getpid())
        time.sleep(2)
        print('%s is done' %os.getpid())
        lock.release()  
  if __name__ == "__main__":
        lock = Lock()
        p = Process(target=work,args=(lock,))
        p.start()

进程同步(队列,推荐使用)

  进程彼此之间互相隔离,要实现进程间通信(IPC),multiprocessing模块支持两种形式:队列和管道,这两种方式都是使用消息传递的
  from multiprocessing import Process,Queue
  import time
  q = Queue(3)
  q.put(3)
  q.put(3)
  q.put(3) 
  q.get() 
  生产者消费者模型:生产者<------>队列<------>消费者,让生产者在生产完毕后,往队列中再发一个结束信号,这样消费者在接收到结束信号后就可以break出死循环   

进程池

  from multiprocessing import Pool
  import os,time
  def work(n):
        print("%s run" % os.getpid())
        time.sleep(3)
        return n**2
  if __name__ == "__main__":
        p = Pool(3)
        res_l = []
        for i in range(10):
              res = p.apply(work,args=(i,)) # apply_saync,异步提交
              res_l.append(res)
        print(res_l)
  回调函数:
        需要回调函数的场景:进程池中任何一个任务一旦处理完了,就立即告知主进程:我好了额,你可以处理我的结果了。主进程则调用一个函数去处理该结果,该函数即回调函数
        p.apply_async(get_page,args=(url,),callback=pasrse_page)
        如果在主进程中等待进程池中所有任务都执行完毕后,再统一处理结果,则无需回调函数

线程概念理解

  1.进程只是用来把资源集中到一起(进程只是一个资源单位,或者说资源集合),而线程才是cpu上的执行单位
  2.线程的创建开销小
  3.多线程共享一个进程的地址空间
  4.线程比进程更轻量级,线程比进程更容易创建可撤销,在许多操作系统中,创建一个线程比创建一个进程要快10-100倍,在有大量线程需要动态和快速修改时,这一特性很有用
  5.若多个线程都是cpu密集型的,那么并不能获得性能上的增强,但是如果存在大量的计算和大量的I/O处理,拥有多个线程允许这些活动彼此重叠运行,从而会加快程序执行的速度

threading模块

  1.开启线程的两种方式
  Ⅰ. from threading import Thread
      if __name__ == "__main__":
          t = Thread(target=func,args=())
          t.start()
  Ⅱ. from threading import Thread
      class T(Thread):
          def __init__(self,):
              super().__init__()
          def run(self): # 函数名必须为run
  2.其他常用方法
  t.join()
  t.daemon = True
  3.GIL
  100个线程去抢GIL锁,即抢执行权限
  肯定有一个线程先抢到GIL(暂且称为线程1),然后开始执行,一旦执行就会拿到lock.acquire()
  极有可能线程1还未运行完毕,就有另外一个线程2抢到GIL,然后开始运行,但线程2发现互斥锁lock还未被线程1释放,于是阻塞,被迫交出执行权限,即释放GIL
  直到线程1重新抢到GIL,开始从上次暂停的位置继续执行,直到正常释放互斥锁lock,然后其他的线程再重复2 3 4的过程

死锁与递归锁

信号量

  与进程池是完全不同的概念,进程池Pool(4),最大只能产生4个进程,而且从头到尾都只是这四个进程,不会产生新的,而信号量是产生一堆线程/进程

Event

条件Condition:使得线程等待,只有满足某条件时,才释放n个线程

定时器

  from threading import Timer
  Timer(5,func)

线程queue

  用法和进程Queue一样   

同步异步提交

  逻辑和进程差不多

协程

原文地址:https://www.cnblogs.com/qijiaxun/p/14216662.html