• 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一样   
    

    同步异步提交

      逻辑和进程差不多
    

    协程

  • 相关阅读:
    东南大学2021年数学分析考研试卷
    东南大学2021年高等代数考研试卷
    东南大学2021年高等代数考研试卷
    东华大学2021年数学分析考研试卷
    东华大学2021年高等代数考研试卷
    [Oracle 工程师手记]探究 Oracle PDB Application Container (二)
    [Oracle 工程师手记]探究 Oracle PDB Application Container (一)
    Jenkins Extended choice parameter和Active Choices param 之Groovy Script使用
    C#异步调用,成功回调中更新界面(线程间操作无效)
    C#使用非托管 DLL 函数
  • 原文地址:https://www.cnblogs.com/qijiaxun/p/14216662.html
Copyright © 2020-2023  润新知