• 我的python学习之路-进程/线程/协程


    本节内容:

      一、进程

        1.1 基本语法与特性

        1.2 join 的使用

        1.3 使用自定义进程类,创建进程

        1.4 守护进程

        1.5 lock 互斥锁

        1.6 信号量 Semaphore

        1.7 事件 Event

        1.8 进程队列

        1.9 生产者消费者模型

        1.10 JoinableQueue 队列

        1.11  Manager 

      二、线程

        2.1 线程的基本使用及特性

        2.2、用类自定义线程

        2.3.线程相关属性

        2.4 线程的缺陷

        2.5 守护线程

        2.6 线程的数据安全

        2.7 Semaphore (线程信号量)

        2.8 互斥锁 _死锁 _递归锁

        2.9 事件 Event

       三、线程队列、线程池、进程池、回调函数

        3.1 线程队列

        3.2  进程池

        3.3  线程池

        3.4  线程池 map

        3.5 总结(进程池、线程池

        3.6 进程池 与 线程池 的 回调函数

      四、 协程

        4.1  利用协程改写生产者消费者模型

        4.2 协程的终极形态 (猴子补丁) 

        4.3 协程方法  

        4.4 协程案例

          

      

    一、进程

    1、进程语法与特性

    (1)进程基本语法

    def func():
        print("1.当前进程子进程id:{} 2.父进程id:{}".format(os.getpid(),os.getppid()) , "<子11>")
    
    if __name__ == "__main__":
        # 1. 创建子进程,返回进程对象
        p = Process(target=func)
        # 2. 调用子进程
        p.start()
        
        # 在主进程中,打印进程id号
        print("1.当前进程子进程id:{} 2.父进程id:{}".format(os.getpid(),os.getppid()),"<主22>")

    (2)创建带有参数的进程

    def func(n):
        for i in range(1,n+1):
            print( i ,  "1.当前进程子进程id:{} 2.父进程id:{}".format(os.getpid(),os.getppid()) , "<子11>")
    
    # 为了兼容linux + windows 下面这句话必须加上;
    if __name__ == "__main__":
        n = 10
        # target = 指定执行的任务   args = 参数元组
        p = Process(target=func, args=(n,))
        p.start()
    
        for i in range(1,n+1):
            print("*" * i )

    (3)进程之间的数据彼此隔离

    num = 10
    def func():
        global num
        num += 1
        print(num,"子进程")
    
    if __name__ == "__main__":
        p = Process(target=func)
        p.start()
        
        time.sleep(3)
        print(num,"主进程")

    (4)进程的异步性

    1.多个进程在执行时,是异步并发程序,因为cpu的调度策略问题,不一定先执行谁后执行谁
    整体的方向,哪个进程更快的分配好资源就先执行;阻塞态就立刻切换

    cpu在执行任务时,如果遇到了阻塞态,会立刻切换到就绪态的任务进行执行
    让整体代码的执行效率最大化

    2.主进程默认会等待所有的子进程执行完毕之后,在关闭程序,释放资源

    孤儿进程:主进程执行结束了,子进程还在运行,占用空间资源,称为孤儿进程
    僵尸进程:子进程执行结束了,主进程没有获取子进程的退出状态,进程释放占用的内存空间
    还持续的在内存中保留,称为僵尸进程

    def func(n):
        print(n , "1.当前进程子进程id:{} 2.父进程id:{}".format(os.getpid(),os.getppid()) , "<子11>")
    
    if __name__ == "__main__":
        for i in range(10):
            p = Process(target=func, args=(i,))
            p.start()
            
        print("主进程执行结束 ... " , os.getpid() )

    2、join使用

    语法: 进程.join()
    意义:必须等待当前这个子进程执行结束之后,再去执行下面的代码
    作用:用来同步子父进程;

    (1)join的基本使用

    def func():
        print("发送第一个短信 : honey 你在么~ ")
        
    if __name__ == "__main__":
        p = Process(target=func)
        p.start()
        # time.sleep(0.01)
        p.join()
        print("发送第二个短信 : 给我点钱,我要买ps5~ ")

    (2)多进程场景

     1 def func(n):
     2     print("发送第一个短信 : honey 你在么~ , 数量{}".format(n))
     3     
     4 if __name__ == "__main__":
     5     lst = []
     6     for i in range(1,5):
     7         p = Process(target=func,args=(i,))
     8         p.start()
     9         # 写法一 : 这种写法会直接导致程序由异步并发,变成同步程序;
    10         # p.join()
    11         # 写法二 : 先异步创建多个进程对象 [异步程序]
    12         lst.append(p)
    13         
    14     # 把所有的进程放到列表中,通过调用join进行管理
    15     # 同步程序
    16     for i in lst:
    17         i.join()
    18         
    19     print("发送最后一个短信 : 球球了,给我买个ps5把")
    View Code

    3、使用自定义进程类,创建进程

    (1) 基本写法

    import os
    class MyProcess(Process):
        def run(self):
            print("1.当前进程子进程id:{} 2.父进程id:{}".format(os.getpid(),os.getppid()) , "<子11>")
    
    if __name__ == "__main__":
        p = MyProcess()
        p.start()
        
        print("主进程 id{}".format(os.getpid()))

    (2) 带有参数的自定义进程类

    class MyProcess(Process):
        def __init__(self,name):
            # 手动调用父类的构造方法,完成成员初始化
            super().__init__()
            self.name = name
            
        def run(self):
            print("1.当前进程子进程id:{} 2.父进程id:{}".format(os.getpid(),os.getppid()) , "<子11>")
            print(self.name)
    
    if __name__ == "__main__":
        p = MyProcess("我是个参数 ... ")
        p.start()
        
        print("主进程 id{}".format(os.getpid()))
        

    4、守护进程

    守护进程守护的是主进程
    当主进程的所有代码执行结束之后,会立刻杀死守护进程

    (1) 基本语法

    from multiprocessing import Process
    import time
    
    def func():
        print("当前任务 启动执行 .... ")
        print("当前任务 结束执行 .... ")
        
    if __name__ == "__main__":
    
        p = Process(target=func)
        # 设置守护进程,在start开始之前.
        p.daemon = True
        
        p.start()
        print("主进程代码执行结束 ...  ")

    (2) 多个子进程的场景

     1 def func1():
     2     print("当前任务 启动执行 .... ")
     3     print("当前任务 结束执行 .... ")
     4     
     5 def func2():
     6     count = 1
     7     while True:
     8         print("*" * count)
     9         time.sleep(0.1)
    10         count += 1
    11         
    12 if __name__ == "__main__":
    13     p1 = Process(target=func1)
    14     p2 = Process(target=func2)
    15     
    16     # 把p2变成守护进程
    17     p2.daemon = True
    18     
    19     p1.start()
    20     p2.start()
    21     
    22     # time.sleep(1)
    23     print("主进程代码执行结束 ... ")
    View Code

    (3) 守护进程用途 : 监控报活

     1 def alive():
     2     while True:
     3         print("10号服务器向总监控服务器报活")
     4         # 间隔一段时间,报活一次
     5         time.sleep(1)
     6         
     7 def func():
     8     while True:
     9         try:
    10             print("10号服务器负责抗住1万用户量的并发访问 ... ")
    11             
    12             # 三秒之后崩溃
    13             time.sleep(3)
    14             
    15             # 主动抛出执行错误的异常
    16             raise RuntimeError
    17         except:
    18             print("10号服务器挂了... 进来维护")
    19             break
    20 
    21 if __name__ == "__main__":
    22     # 当前进程负责报活
    23     p1 = Process(target=alive)
    24     # 当前进程抗主用户并发
    25     p2 = Process(target=func)
    26 
    27     p1.daemon = True
    28     p1.start()
    29     p2.start()
    30     
    31     # 必须等待2号任务执行完毕之后,在放行
    32     p2.join()
    33     
    34     print("主进程执行结束 ... ")
    View Code

    5、锁 lock 互斥锁

    上锁和解锁是一对
    不能只上锁不解锁,会发生死锁现象(阻塞)
    只有在解锁状态下,下一个进程才有机会上锁

    (1)基本用法

    from multiprocessing import Lock,Process
    import json,random,time
    # 创建一把锁
    lock = Lock()
    # 上锁
    lock.acquire()
    # ... ... .... 
    # 解锁
    lock.release()

    (2)12306抢票

     1 # 1.读写数据的票数
     2 def wr_info(sign,dic=None):
     3     if sign == "r":
     4         # 读取票数
     5         with open("data.txt",mode="r",encoding="utf-8") as fp:    
     6             dic = json.load(fp)
     7         return dic
     8     elif sign == "w":
     9         # 写入票数
    10         with open("data.txt",mode="w",encoding="utf-8") as fp:
    11             json.dump(dic,fp)
    12         
    13 
    14 # res = wr_info("r")
    15 # print(res , type(res))
    16 # dic = {"count":0}
    17 # res = wr_info("w",dic)
    18 
    19 # 2.执行抢票方法
    20 def get_ticket(person):
    21     # 查看数据库票数
    22     dic = wr_info("r")
    23     
    24     # 模拟网络延迟
    25     time.sleep(random.uniform(0.1,0.8))
    26     
    27     # 判断票数是否能抢
    28     if dic["count"] > 0 :
    29         print("恭喜你~{}抢票成功~".format(person))
    30         # 抢到票数之后,数量上减1
    31         dic["count"] -= 1
    32         # 更新数据中的票数
    33         wr_info("w",dic)
    34     else:
    35         print("抱歉~{}没有抢到票~".format(person))
    36         
    37 
    38 def main(person,lock):
    39     
    40     # 看一看有几张票
    41     dic = wr_info("r")
    42     print("{}查看票数剩余数量为:{}".format(person,dic["count"]))
    43 
    44     # 上锁(保证同一时间,只有一个进程修改数据)
    45     lock.acquire()
    46     # 真正开始下单
    47     get_ticket(person)
    48     # 解锁(解锁之后,别的进程才有机会进来改数据)
    49     lock.release()
    50     
    51 if __name__ == "__main__":
    52     # 创建一个锁对象
    53     lock = Lock()
    54     
    55     lst = ["尤佳","黄常见","张银","孙翔宇","家营和","耿择时","薛宇健","朱培峰","李琦","于盛林"]
    56     for i in lst:
    57         p = Process(target=main,args=(i,lock))
    58         p.start()
    View Code

    6、信号量 Semaphore

    本质上是锁,同一时间,多个进程上多把锁,可以控制上锁的数量

    (1)基本用法

    from multiprocessing import Semaphore , Process
    import time,random
    """
    # 同一时间允许多个进程上4把锁
    sem = Semaphore(4)
    # 上锁
    sem.acquire()
    # ....
    # 解锁 
    sem.release()

    (2)小练习

     1 def singsong_gefang(person,sem):
     2     # 上锁
     3     sem.acquire()
     4     # 唱歌
     5     print("{}进入歌房唱歌 ... ".format(person))
     6     # 唱一段时间
     7     time.sleep(random.randrange(3,9))
     8     # 离开歌房
     9     print("{}离开歌房不唱了 ... ".format(person))
    10     # 解锁
    11     sem.release()
    12     
    13 if __name__ == "__main__":
    14     
    15     sem = Semaphore(4)
    16     lst = ["王生福","敬旭阳","小黄人","梁瑞卿","郝建康","杨特","白金鸽","王钊","付家庆","何子豪","菲菲"]
    17     for i in lst:
    18         p = Process(target=singsong_gefang ,args=(i,sem))
    19         p.start()
    View Code

    7、事件 (Event)

     阻塞事件 :
    e = Event()生成事件对象e
    e.wait()动态给程序加阻塞 , 程序当中是否加阻塞完全取决于该对象中的is_set() [默认返回值是False]
       如果是True 不加阻塞
        如果是False 加阻塞

    控制这个属性的值
      set()方法 将这个属性的值改成True
      clear()方法 将这个属性的值改成False
      is_set()方法 判断当前的属性是否为True (默认上来是False)

    (1)基本语法

    1 # 生成事件对象e 
    2 e = Event()
    3 # is_set方法获取的值为False(默认值)
    4 """False => 阻塞 True => 非阻塞"""
    5 print(e.is_set())
    6 e.wait()
    7 print("代码执行了 .... ")
    语法1
     1 from multiprocessing import Process , Event
     2 import time,random
     3 # 生成事件对象e 
     4 e = Event()
     5 # 将这个属性的值改成True
     6 e.set()
     7 e.wait()
     8 print("代码执行了1 .... ")
     9 
    10 e.clear()
    11 e.wait()
    12 print("代码执行了2 .... ")
    语法2
    1 # 生成事件对象e 
    2 e = Event()
    3 # 代码最多阻塞3秒在放行...
    4 e.wait(3)
    5 print("代码执行了3 .... ")
    语法3

    (2)模拟红绿灯

     1 from multiprocessing import Process , Event
     2 import time,random
     3 
     4 def traffic_light(e):
     5     print("红灯亮")
     6     while True:
     7         if e.is_set(): # True
     8             # 绿灯状态
     9             time.sleep(1)
    10             # 亮1秒 , 绿灯 -> 红灯
    11             print("红灯亮")
    12             e.clear()
    13         else:
    14             # 红灯状态
    15             time.sleep(1)
    16             # 亮1秒 , 红灯 -> 绿灯
    17             print("绿灯亮")
    18             e.set()
    19 # e = Event()
    20 # traffic_light(e)
    21 
    22 # 模拟小车
    23 """
    24 e.is_set() => True  绿灯
    25 e.is_set() => False 红灯
    26 """
    27 def car(e,i):
    28     # 当红灯时,小车阻塞
    29     if e.is_set() == False : # e.is_set => False  => not False
    30         print("当前小车car{}在等待 ... ".format(i))
    31         e.wait()
    32     
    33     # 当绿灯时,小车放行
    34     print("当前小车car{} 放行了 ... ".format(i))
    35 
    36 
    37 # 全国交通灯
    38 """
    39 if __name__ == "__main__":
    40     e = Event()
    41     # 创建交通灯
    42     p1 = Process(target=traffic_light , args=(e,))
    43     p1.start()
    44     
    45     # 创建小车进程
    46     for i in range(1,21):
    47         time.sleep(random.randrange(3))
    48         p2 = Process(target=car,args=(e,i))
    49         p2.start()
    50 """
    51 # 包头交通灯 (在车全部跑完的时,把交通灯炸掉,省点电)
    52 if __name__ == "__main__":
    53     lst = []
    54     e = Event()
    55     # 创建交通灯
    56     p1 = Process(target=traffic_light , args=(e,))
    57     
    58     # 把交通灯这个进程变成守护进程
    59     p1.daemon = True    
    60     p1.start()
    61     
    62     # 创建小车进程
    63     for i in range(1,21):
    64         time.sleep(random.randrange(3))
    65         p2 = Process(target=car,args=(e,i))
    66         lst.append(p2)
    67         p2.start()
    68         
    69         
    70     
    71     # 必须让所有的小车跑完,在杀死交通灯进程
    72     for i in lst:
    73         i.join()        
    74         
    75     print("关闭交通灯")
    View Code

    8、进程队列

    特点:前进先出 , 后进后出

    (1)基本语法

     1 q = Queue()
     2 print(q)
     3 # (1)put() 存放
     4 q.put(100)
     5 q.put(200)
     6 q.put(300)
     7 
     8 # (2)get() 获取
     9 """
    10 res = q.get()
    11 res = q.get()
    12 res = q.get()
    13 # 在获取不到任何数据时候,直接阻塞
    14 res = q.get()
    15 print(res)
    16 """
    17 
    18 # (3)get_nowait() 拿不到报异常
    19 """get_nowait 目前版本存在兼容性问题,时好时坏,[linux目前不支持]"""
    20 """
    21 res = q.get_nowait()
    22 res = q.get_nowait()
    23 res = q.get_nowait()
    24 # res = q.get_nowait() error
    25 print(res)
    26 """
    27 
    28 # (4)put_nowait() 非阻塞版本的put
    29 '''设定队列的长度为3 , 最多放3个数据'''
    30 q2 = Queue(3)
    31 # 如果队列已经满了,在存储数据直接报错
    32 # q2.put_nowait(1000)
    33 # q2.put_nowait(2000)
    34 # q2.put_nowait(3000)
    35 # q2.put_nowait(4000)
    36 
    37 try:
    38     q2.put_nowait(1000)
    39     q2.put_nowait(2000)
    40     q2.put_nowait(3000)
    41     q2.put_nowait(4000)
    42 except:
    43     pass
    44 
    45 # 如果队列已经满了,在存储数据直接阻塞
    46 # q2.put(1)
    47 # q2.put(2)
    48 # q2.put(3)
    49 # q2.put(4)
    View Code

    (2)IPC通信 

     1 def func(q):
     2     # 2.子进程获取主进程存放的数据
     3     res = q.get()
     4     print(res , "<子进程>")
     5     # 3.子进程存储数据
     6     q.put("王娟娟")
     7 
     8 if __name__ == "__main__":
     9     q3 = Queue()
    10     p = Process(target=func,args=(q3,))
    11     p.start()
    12     
    13     # 1.主进程存储数据
    14     q3.put("荷叶")
    15     
    16     # 为了让主进程获取到数据,必须等待子进程执行完毕之后,在向下执行;
    17     p.join()    
    18     
    19     # 4.让主进程获取数据
    20     res = q3.get()
    21     print(res,"<主进程>")
    View Code

    9、生产者消费者模型

    爬虫
      1号进程负责向队列中存储数据,整体存储响应的一篇篇的小说,文章...
      2号进程负责从队列中获取数据,匹配文章标题,时间,作者 ...

      1号进程可以理解成生产者
      2号进程可以理解成消费者

    从程序上看
      生产者负责存储数据 (put)
      消费者负责获取数据 (get)

    理想的生产者消费者模型:
      1.生产多少,消费多少
      2.生产数据的速度与消费数据的速度相对一致

    (1)基础版-生产者消费者模型 

     1 from multiprocessing import Queue,Process
     2 import time,random
     3 # 生产者
     4 def producer(q,name,food):
     5     for i in range(1,6):
     6         time.sleep(random.uniform(0.1,1))
     7         res = "第{}碗{}".format(i,food)
     8         print( "{}生产了{}".format(name,res) )
     9         # 存储白米粥到队列中
    10         q.put(res)
    11     
    12 # 消费者
    13 def consumer(q,name):
    14     while True:
    15         time.sleep(random.uniform(0.1,1))
    16         # 获取队列中的白米粥
    17         food = q.get()
    18         print("{}吃了{}".format(name,food))
    19         
    20 
    21 if __name__ == "__main__":
    22     q = Queue()
    23     # 生产者
    24     p1 = Process( target = producer , args = (q , "张银" , "白米粥"))
    25     # 消费者
    26     p2 = Process( target = consumer , args = (q , "郝建康" ))
    27     
    28     p1.start()
    29     p2.start()
    View Code

    (2)升级版 (消费者是死循环,要给与终止)

     1 from multiprocessing import Queue,Process
     2 import time,random
     3 # 生产者
     4 def producer(q,name,food):
     5     for i in range(1,6):
     6         time.sleep(random.uniform(0.1,1))
     7         res = "第{}碗{}".format(i,food)
     8         print( "{}生产了{}".format(name,res) )
     9         # 存储白米粥到队列中
    10         q.put(res)
    11     
    12 # 消费者
    13 def consumer(q,name):
    14     while True:
    15         # 获取队列中的白米粥
    16         food = q.get()
    17         if food is None:
    18             break    
    19         time.sleep(random.uniform(0.1,1))
    20         print("{}吃了{}".format(name,food))
    21 
    22 if __name__ == "__main__":
    23     q = Queue()
    24     # 生产者
    25     p1 = Process( target = producer , args = (q , "张银" , "白米粥"))
    26     # 消费者
    27     p2 = Process( target = consumer , args = (q , "郝建康" ))
    28     
    29     p1.start()
    30     p2.start()
    31     
    32     # 生产结束的最后放入None 关键字 代表生产结束
    33     p1.join()
    34     q.put(None)
    View Code
     1 # 消费者
     2 def consumer(q,name):
     3     while True:
     4         # 获取队列中的白米粥
     5         food = q.get()
     6         if food is None:
     7             break    
     8         time.sleep(random.uniform(0.1,1))
     9         print("{}吃了{}".format(name,food))
    10 
    11 if __name__ == "__main__":
    12     q = Queue()
    13     # 生产者
    14     p1 = Process( target = producer , args = (q , "张银" , "白米粥"))
    15     p1_1 =  Process( target = producer , args = (q , "王永捐" , "银耳粥"))
    16     
    17     # 消费者
    18     p2 = Process( target = consumer , args = (q , "郝建康" ))
    19     p2_2 = Process( target = consumer , args = (q , "李琦" ))
    20     
    21     p1.start()
    22     p1_1.start()
    23     p2.start()
    24     p2_2.start()
    25     
    26     # 1号生产者 , 生产结束的最后放入None 关键字 代表生产结束
    27     p1.join()
    28     # 2号生产者 , 生产结束的最后放入None 关键字 代表生产结束
    29     p1_1.join()
    30     
    31     
    32     
    33     # 1号生产者 放入None
    34     q.put(None)
    35     # 2号生产者 放入None
    36     q.put(None)
    View Code

    10、JoinableQueue 队列

    put 存放 计数器属性值+1
    get 获取
    task_done 计数器属性值-1
    join 配合task_done来使用(负责加阻塞)

    put 一次数据, 队列的内置计数器+1
    task_done 一次数据 队列的内置计数器-1
    当内置计数器值为0时 , join就会放行;

    计数器值=0
    队列.join() 放行
    计数器值非0
    队列.join() 阻塞

    (1)、基本用法

     1 from multiprocessing import JoinableQueue
     2 jq = JoinableQueue()
     3 # 队列计数器+1
     4 jq.put("朱培峰")
     5 # 队列计数器+1
     6 jq.put("薛宇健")
     7 
     8 print(jq.get())
     9 # 队列计数器-1
    10 jq.task_done()
    11 
    12 print(jq.get())
    13 # 队列计数器-1
    14 jq.task_done()
    15 
    16 jq.join()
    17 print("代码执行了 .... ")
    View Code

    (2)对生产者消费者模型进行改造

     1 from multiprocessing import JoinableQueue,Process
     2 import time,random
     3 # 生产者
     4 def producer(q,name,food):
     5     for i in range(1,6):
     6         time.sleep(random.uniform(0.1,1))
     7         res = "第{}碗{}".format(i,food)
     8         print( "{}生产了{}".format(name,res) )
     9         # 存储白米粥到队列中
    10         # 内置计数器 +1 => 5
    11         q.put(res)
    12     
    13 # 消费者
    14 def consumer(q,name):
    15     while True:
    16         time.sleep(random.uniform(0.1,1))
    17         # 获取队列中的白米粥
    18         food = q.get()
    19         print("{}吃了{}".format(name,food))
    20         # 内置计数器 -1 => 0
    21         q.task_done()
    22         
    23 
    24 if __name__ == "__main__":
    25     jq = JoinableQueue()
    26     # 生产者
    27     p1 = Process( target = producer , args = (jq , "张银" , "白米粥"))
    28     # 消费者
    29     p2 = Process( target = consumer , args = (jq , "郝建康" ))
    30     p2.daemon = True
    31     
    32     p1.start()
    33     p2.start()
    34     
    35     # 必须等到生产者全部生产完毕之后,在放行(带来的问题,消费者消费不完全)
    36     p1.join()
    37     # 必须让消费者全部吃完
    38     jq.join()
    39     print( " 主进程执行结束 .."  )
    View Code

    11、 Manager (list 列表 dict 字典)

     1 from multiprocessing import Process, Manager , Lock
     2 
     3 def work(data,lock):
     4     """
     5     # 共享字典
     6     lock.acquire()
     7     data["count"] +=1
     8     lock.release()
     9     """    
    10     # 共享列表
    11     """
    12     lock.acquire() + lock.release() => with 自动完成
    13     """
    14     with lock:
    15         data[0] -= 1
    16     
    17 if __name__ == "__main__":
    18     lst = []
    19     lock = Lock()
    20     m = Manager()
    21     # 创建一个多进程之间共享数据的字典
    22     # data = m.dict( {"count":0} )
    23     # 创建一个多进程之间共享数据的列表
    24     data = m.list( [100,200,300] )
    25     # {'count': 1000} <class 'multiprocessing.managers.DictProxy'>
    26     print(data , type(data))
    27     
    28     
    29     """ 进程数超过1000 , 电脑蓝屏 , 谨慎使用 """
    30     for i in range(10):
    31         p = Process(target=work,args=(data,lock))
    32         p.start()
    33         lst.append(p)
    34     
    35     # 必须等待所有子进程执行完毕之后,在放行,打印最后数据(否则直接报错)
    36     for i in lst:
    37         i.join()
    38     
    39     print(data)
    View Code

    二 、线程 

    1、线程的基本使用及特性

    进程是系统资源分配的最小单位
    线程是计算机中调度的最小单位
    一个进程里至少一个主线程;

    (1)一个进程里面包含多个线程,线程之间是异步并发操作;

    from threading import Thread
    import time , random , os
    def func(i):
        time.sleep(random.uniform(0.1,0.9))
        print(i, "当前进程号:{}".format(os.getpid()) )
        
    if __name__ == "__main__":
        for i in range(10):
            t = Thread(target=func,args=(i,))
            t.start()
    
        print(os.getpid())

    (2) 多进程和多线程谁的速度快? 多线程

     1 def func(i):
     2     print("当前进程号:{} , 参数{}".format(os.getpid(),i))
     3 
     4 if __name__ == "__main__":
     5     """
     6     lst = []
     7     starttime = time.time()
     8     for i in range(500):
     9         t = Thread(target=func,args=(i,))
    10         t.start()
    11         lst.append(t)
    12         
    13     for i in lst:
    14         i.join()
    15         
    16     endtime = time.time()
    17     print("运行时间{}".format(endtime -  starttime )) # 0.09600567817687988
    18     """
    19 
    20     lst = []
    21     starttime = time.time()
    22     for i in range(500):
    23         p = Process(target=func,args=(i,))
    24         p.start()
    25         lst.append(p)
    26         
    27     for i in lst:
    28         i.join()
    29         
    30     endtime = time.time()
    31     print("运行时间{}".format(endtime -  starttime )) # 运行时间17.507001399993896
    View Code

    (3) 多线程之间的数据彼此共享

     1 num = 1000
     2 lst = []
     3 def func():
     4     global num
     5     num -= 1
     6     
     7 for i in range(1000):
     8     t = Thread(target=func)
     9     t.start()
    10     lst.append(t)
    11     
    12 for i in lst:
    13     i.join()
    14     
    15 print(num)
    View Code

    2、用类自定义线程

     1 from threading import Thread
     2 import os , time
     3 class MyThread(Thread):
     4 
     5     def __init__(self,name):
     6         # 必须手动调用一下父类的构造芳芳
     7         super().__init__()
     8         # 添加参数到成员属性name 当中
     9         self.name = name
    10         
    11     def run(self):
    12         print("当前进程号:{} , 参数{}".format(os.getpid(),self.name))
    13         
    14 if __name__ == "__main__":
    15     t = MyThread("我是石磊")
    16     t.start()
    17     
    18     # 用来同步主和子线程的
    19     # print(t)
    20     t.join()
    21     print("主线程执行结束 ... ")
    View Code

    3.线程相关属性

    线程.is_alive() 检测线程是否仍然存在
    线程.setName() 设置线程名字
    线程.getName() 获取线程名字
    1.currentThread().ident 查看线程id号
    2.enumerate() 返回目前正在运行的线程列表
    3.activeCount() 返回目前正在运行的线程数量

     1 def func():
     2     time.sleep(3)
     3 
     4 """
     5 if __name__ == "__main__":
     6     t = Thread(target=func)
     7     t.start()
     8     # 检测线程是否仍然存在
     9     res = t.is_alive()
    10     print(t)
    11     print(res)
    12     # 获取线程名字
    13     print(t.getName())    
    14     # 设置线程名字
    15     t.setName("下载线程")
    16     print(t.getName())    
    17 """
    18 
    19 from threading import currentThread
    20 from threading import enumerate 
    21 from threading import activeCount
    22 
    23 def func():
    24     time.sleep(3)
    25     print("当前子线程的线程号是{},进程号是{}".format(  currentThread().ident , os.getpid()   )  )
    26     # print(t)
    27     
    28 if __name__ == "__main__":
    29     '''
    30     t = Thread(target=func)
    31     t.start()    
    32     print("当前主线程的线程号是{},进程号是{}".format(  currentThread().ident , os.getpid()   )  )
    33     # print(t)
    34     '''
    35     for i in range(10):
    36         t = Thread(target=func)
    37         t.start()
    38     
    39     # 2.返回目前正在运行的线程列表
    40     lst = enumerate()
    41     # MainThread, Thread-1 Thread-2 .... 
    42     print(lst  , len(lst))
    43     # 3.activeCount()      返回目前正在运行的线程数量 (了解)
    44     # print(activeCount())
    45     
    View Code

    4.线程的缺陷

    GIL:
      全局解释器锁
    效果:
      同一时间,一个进程下的多个线程只能被一个cpu执行,不能实现线程的并行操作
    原因:
      1.历史原因
      2.python是解释型语言
    改善方法:
      1.用多进程间接实现线程的并行
      2.换一个Pypy,Jpython解释器

    目前来看,还不能根本解决;
    对于io密集型任务,python 绰绰有余.

    5、守护线程

    等待所有线程全部执行完毕之后,自己在终止程序;守护所有线程

     1 from threading import Thread
     2 import time
     3 def func1():
     4     while True:
     5         time.sleep(1)
     6         print("我是 执行任务1 ... ")
     7 
     8 def func2():
     9     print("我是 执行任务2 ... start ")
    10     time.sleep(5)
    11     print("我是 执行任务2 ... end ")
    12     
    13 def func3():
    14     print("我是 执行任务3 ... start ")
    15     time.sleep(8)
    16     print("我是 执行任务3 ... end ")
    17     
    18 if __name__ == "__main__":
    19 
    20     t1 = Thread(target=func1)
    21     t2 = Thread(target=func2)
    22     t3 = Thread(target=func3)
    23     
    24     # 给t1这个线程设置为守护线程
    25     t1.setDaemon(True)
    26     
    27     
    28     t1.start()
    29     t2.start()
    30     t3.start()
    31     
    32     print("主线程执行结束 ... ")
    View Code

    6、线程的数据安全

     1 from threading import Thread , Lock
     2 import time
     3 total = 0
     4 # 尽量降低换锁的频率,提高代码整体的执行效率
     5 def func1(lock):
     6     # 加锁方法1
     7     global total    
     8     lock.acquire()
     9     for i in range(100000):
    10         total += 1
    11     lock.release()
    12         
    13 def func2(lock):
    14     # 加锁方法2
    15     global total     
    16     with lock:
    17         for i in range(100000):        
    18             total -= 1
    19 
    20 
    21 
    22 if __name__ == "__main__":
    23     lst = []
    24     lock = Lock()
    25     startime = time.time()
    26     for i in range(10):
    27         # 创建10个子线程 +1
    28         t1 = Thread(target=func1,args=(lock,))
    29         t1.start()
    30         
    31         # 创建10个子线程 -1
    32         t2 = Thread(target=func2,args=(lock,))
    33         t2.start()
    34         
    35         lst.append(t1)
    36         lst.append(t2)
    37 
    38     for i in lst:
    39         i.join()
    40     
    41     endtime = time.time()
    42 
    43     
    44     print("主线程执行结束了 , total的结果是{}".format(total))
    45     # 所用的时间是3.6152114868164062
    46     print("所用的时间是{}".format(endtime - startime))
    View Code

    7、Semaphore (线程信号量)

     1 from threading import Semaphore , Thread
     2 import time,random
     3 
     4 def func(i,sem):
     5     time.sleep(random.uniform(0.1,0.9))
     6     """
     7     with 语法 自动上锁,自动解锁;
     8     """
     9     with sem:
    10         print("{}号病人,你在治疗痔疮".format(i))
    11         time.sleep(random.uniform(3,9))
    12         print("{}号病人,一瘸一拐的走出了房间".format(i))
    13         
    14 
    15 if __name__ == "__main__":
    16     sem = Semaphore(4)
    17     for i in range(8):
    18         Thread(target=func,args=(i,sem)).start()
    19 
    20     print(" 主线程执行结束 ...  ")
    View Code

    8、互斥锁 _死锁 _递归锁

    (1) 语法上的死锁

    只上锁,不解锁是死锁 , 程序阻塞

     1 from threading import Thread , Lock , RLock
     2 import time
     3 # 场景一
     4 """
     5 lock = Lock()
     6 lock.acquire()
     7 # lock.release()
     8 lock.acquire()
     9 print("代码执行了...")
    10 # lock.release()
    11 """
    12 # 场景二 注意两把不同的锁同时上锁,可能造成逻辑死锁.
    13 """
    14 lock1 = Lock()
    15 lock2 = Lock()
    16 lock1.acquire()
    17 lock2.acquire()
    18 print("代码执行了 ... ")
    19 lock2.release()
    20 lock1.release()
    21 """
    View Code

    (2) 逻辑上的死锁

     1 from threading import Thread , Lock , RLock
     2 import time
     3 noodle_lock = Lock()
     4 kuaizi_lock = Lock()
     5 
     6 def eat1(name):
     7     noodle_lock.acquire()
     8     print("{}抢到了面条...".format(name))
     9     kuaizi_lock.acquire()
    10     print("{}抢到了筷子...".format(name))
    11     
    12     print("开始吃面条~ 老北京杂酱面 ")
    13     time.sleep(0.5)
    14     
    15     kuaizi_lock.release()
    16     print("{}放下了筷子...".format(name))
    17     noodle_lock.release()
    18     print("{}放下了面条...".format(name))
    19     
    20 # eat1("郝建康")
    21     
    22 def eat2(name):
    23 
    24     kuaizi_lock.acquire()
    25     print("{}抢到了筷子...".format(name))
    26     noodle_lock.acquire()
    27     print("{}抢到了面条...".format(name))
    28     
    29     print("开始吃面条~ 老北京杂酱面 ")
    30     time.sleep(0.5)    
    31     
    32     noodle_lock.release()
    33     print("{}放下了面条...".format(name))
    34     kuaizi_lock.release()
    35     print("{}放下了筷子...".format(name))
    36 
    37 # eat2("李琦")
    38 if __name__ == "__main__":
    39     lst1 = ["李琦","朱培峰"]
    40     lst2 = ["郝建康","菲菲"]
    41     
    42     for name in lst1:
    43         Thread(target=eat1,args=(name,)).start()
    44         
    45     for name in lst2:
    46         Thread(target=eat2,args=(name,)).start()
    View Code

    (3) 使用递归锁

    递归锁的提出专门用来解决死锁现象

    用于快速解决线上死锁现象,连续上锁形同虚设,可以快速解开.

     1 from threading import Thread , Lock , RLock
     2 import time
     3 noodle_lock = kuaizi_lock = RLock()
     4 def eat1(name):
     5     noodle_lock.acquire()
     6     print("{}抢到了面条...".format(name))
     7     kuaizi_lock.acquire()
     8     print("{}抢到了筷子...".format(name))
     9     
    10     print("开始吃面条~ 老北京杂酱面 ")
    11     time.sleep(0.5)
    12     
    13     kuaizi_lock.release()
    14     print("{}放下了筷子...".format(name))
    15     noodle_lock.release()
    16     print("{}放下了面条...".format(name))
    17     
    18 # eat1("郝建康")
    19     
    20 def eat2(name):
    21 
    22     kuaizi_lock.acquire()
    23     print("{}抢到了筷子...".format(name))
    24     noodle_lock.acquire()
    25     print("{}抢到了面条...".format(name))
    26     
    27     print("开始吃面条~ 老北京杂酱面 ")
    28     time.sleep(0.5)    
    29     
    30     noodle_lock.release()
    31     print("{}放下了面条...".format(name))
    32     kuaizi_lock.release()
    33     print("{}放下了筷子...".format(name))
    34 
    35 # eat2("李琦")
    36 if __name__ == "__main__":
    37     lst1 = ["李琦","朱培峰"]
    38     lst2 = ["郝建康","菲菲"]
    39     
    40     for name in lst1:
    41         Thread(target=eat1,args=(name,)).start()
    42         
    43     for name in lst2:
    44         Thread(target=eat2,args=(name,)).start()
    View Code

    (4) 尽量使用一把锁解决问题,(不要使用锁嵌套,容易逻辑死锁.)

     1 from threading import Thread , Lock , RLock
     2 import time
     3 lock = Lock()
     4 def eat1(name):
     5     lock.acquire()
     6     print("{}抢到了面条...".format(name))
     7     print("{}抢到了筷子...".format(name))
     8     
     9     print("开始吃面条~ 老北京杂酱面 ")
    10     time.sleep(0.5)    
    11 
    12     print("{}放下了筷子...".format(name))    
    13     print("{}放下了面条...".format(name))
    14     lock.release()    
    15 
    16 def eat2(name):
    17     lock.acquire()
    18     print("{}抢到了筷子...".format(name))
    19     print("{}抢到了面条...".format(name))
    20     
    21     print("开始吃面条~ 老北京杂酱面 ")
    22     time.sleep(0.5)        
    23 
    24     print("{}放下了面条...".format(name))
    25     print("{}放下了筷子...".format(name))
    26     lock.release()
    27 
    28 if __name__ == "__main__":
    29     lst1 = ["李琦","朱培峰"]
    30     lst2 = ["郝建康","菲菲"]
    31     
    32     for name in lst1:
    33         Thread(target=eat1,args=(name,)).start()
    34         
    35     for name in lst2:
    36         Thread(target=eat2,args=(name,)).start()
    View Code

    9、事件 Event

    wait   : 动态加阻塞 (True => 放行 False => 阻塞)

    is_set : 获取内部成员属性值 (True , False 默认为False) 

    clear  : 把成员属性值 => False 

    set    : 把成员属性值 => True

     1 from threading import Thread , Event
     2 import time , random
     3 
     4 def check(e):
     5     # 检测中
     6     print("检测中 .... ")
     7     time.sleep(1)
     8     print("检测账号中 ... ")
     9     time.sleep(1)
    10     print("检测密码中 .... ")
    11     time.sleep(random.randrange(1,4)) # 3 4 5
    12     # 检测ok , 直接放行
    13     e.set()    
    14     
    15 # 连接线程
    16 def connect(e):
    17     # 默认没有连接成功
    18     sign = False
    19     for i in range(1,4):
    20         # 最多阻塞1秒
    21         e.wait(1)
    22         if e.is_set():
    23             print("连接远程数据库 ok .... ")
    24             # sign = True 代表成功
    25             sign = True
    26             break
    27         else:
    28             print("尝试连接数据库第{}次失败".format(i))
    29             
    30             
    31     # 如果没成功,抛出超时异常
    32     if sign == False:
    33         raise TimeoutError
    34 
    35 if __name__ == "__main__":
    36     e = Event()
    37     # 启动检测线程
    38     t = Thread(target=check,args=(e,))
    39     t.start()
    40     
    41     # 启动连接线程
    42     t = Thread(target=connect,args=(e,))
    43     t.start()
    44     
    View Code

     三、线程队列、线程池、进程池

    1、线程队列

    相关函数: 

    put   存放 超出队列长度阻塞
    get 获取 超出队列长度阻塞
    put_nowait 存放 超出队列长度报错
    get_nowait 获取 超出队列长度报错

    (1)Queue

    特征:先进先出,后进后出

    场景一

    from queue  import Queue
    q = Queue()
    q.put(111)
    q.put(222)
    q.put(333)
    print(q.get())
    print(q.get())
    print(q.get())
    # print(q.get())
    
    # error
    # print(q.get_nowait())

    场景二

    # 队列中最多存放3个元素
    q = Queue(3)
    q.put(1)
    q.put(2)
    q.put(3)
    # 超出队列长度阻塞
    # q.put(4)
    # 超出队列长度报错
    #q.put_nowait(444)

    (2) LifoQueue

    特征:先进后出 , 后进先出

    from queue import LifoQueue
    lq = LifoQueue()
    lq.put(999)
    lq.put(998)
    lq.put(997)
    print(lq.get())
    print(lq.get())
    print(lq.get())
    """
    997
    998
    999
    """

    (3) PriorityQueue

    特征:按照优先级顺序进行排序存放数据(默认从小到大)

    1).对数字进行排序 (默认从小到大)
    from queue import PriorityQueue
    pq = PriorityQueue()
    pq.put(11)
    pq.put(-100)
    pq.put(100)
    pq.put(-34)
    print(pq.get()) ##-100
    print(pq.get()) #-34
    print(pq.get()) #11
    print(pq.get()) # 100
     2.)对字母进行排序
     1 from queue import PriorityQueue
     2 pq = PriorityQueue()
     3 pq.put("wangwen")
     4 pq.put("liyaqi")
     5 pq.put("liqi")
     6 pq.put("wangyuhan")
     7 pq.put("王文")
     8 pq.put("王雨涵")
     9 pq.put("李琦")
    10 print( pq.get() )
    11 print( pq.get() )
    12 print( pq.get() )
    13 print( pq.get() )
    14 print( pq.get() )
    15 print( pq.get() )
    16 print( pq.get() )
    View Code
    3).对容器进行排序
     1 pq.put( (18,"wangwen")  )
     2 pq.put( (19,"wangyuhan") )
     3 pq.put( (17,'wuhongchang') )
     4 pq.put( (90,"sunxiangyu") )
     5 pq.put( (90,"zhangyin") )
     6 
     7 print(pq.get())
     8 print(pq.get())
     9 print(pq.get())
    10 print(pq.get())
    11 print(pq.get())
    View Code
    4).注意点

    队列里面只能存放同一类型的数据,不能混杂其他类型数据

    2、进程池

    获取处理器的逻辑核心数: os.cpu_count()

    多条进程提前在进程池当中开辟了,可以触发并发,也可以触发并行效果

     1 from concurrent.futures import ProcessPoolExecutor 
     2 import os,time,random
     3 def func(i):
     4     time.sleep(random.uniform(0.1,0.9))
     5     print(os.getpid())
     6     print("此时任务在执行 .... ")
     7     print("任务结束了 ... ")
     8     print(i)
     9     # time.sleep(10)
    10     return i
    11 
    12 if __name__ == "__main__":
    13     lst = []
    14 
    15     # (1) 创建进程池对象
    16     """ProcessPoolExecutor( 默认参数是cpu的最大逻辑核心数 )"""
    17     p = ProcessPoolExecutor()
    18     # print(p)
    19     
    20     # (2) 异步提交任务
    21     """语法:submit(任务,参数1,参数2,参数3 .... )"""
    22     """含义:进程池里面有4个进程提前开辟好了,执行10个任务"""
    23     """
    24         注意点:默认如果一个进程短时间内可完成更多任务,
    25         进程池就不会用更多进程辅助完成,可以节省系统资源;
    26     """
    27     for i in range(10):
    28         obj = p.submit( func, i)
    29         # print(obj , "<===1===>")
    30         # result 不要加这里
    31         # print(obj.result() , "<==2==>")
    32         lst.append(obj)
    33 
    34     # (3) 获取当前任务的返回值 (加了result之后,会变成同步程序)
    35     # for i in lst:
    36         # print(i.result() ,"<==3==>")
    37         
    38         
    39     # (4) shutdown 等待进程池所有任务执行完毕之后,在放行;作用:同步进程池和主进程;
    40     p.shutdown()
    41     
    42     print("进程池结束 ... ")
    View Code

    3、线程池

     1 from concurrent.futures import  ThreadPoolExecutor
     2 import os,time,random
     3 from threading import currentThread as ct
     4 
     5 def func(i):
     6     print("线程任务执行中.... ")
     7     time.sleep(1)
     8     print("线程任务结束了 .... ")
     9     # 获取线程号    
    10     return ct().ident
    11 
    12 if __name__ == "__main__":
    13     lst = []
    14     setvar = set()
    15     # (1)创建线程池对象
    16     t = ThreadPoolExecutor() # os.cpu_count() * 5  进程:线程 是 1:5配比
    17     # print(t)
    18     
    19     
    20     # (2) 异步提交任务
    21     """语法:submit(任务,参数1,参数2,参数3 .... )"""
    22     """含义:进程池里面有4个进程提前开辟好了,执行4*5=20个线程"""
    23     """
    24         注意点:默认如果一个线程短时间内可完成更多任务,
    25         线程池就不会用更多进程辅助完成,可以节省系统资源;
    26     """
    27     for i in range(100):
    28         obj = t.submit(func,i)
    29         # print(obj)
    30         # obj.result()  (加了result之后,会变成同步程序)
    31         lst.append(obj)
    32         
    33     # (3) 获取当前任务的返回值
    34     for i in lst:
    35         setvar.add(i.result()) 
    36         
    37     # (4) shutdown  等待线程池所有线程执行完毕之后,在放行
    38     t.shutdown()
    39     
    40     print(setvar , len(setvar))
    View Code

     4、线程池 map

     1 from concurrent.futures import  ThreadPoolExecutor
     2 from threading import currentThread as ct
     3 from collections import Iterator,Iterable
     4 def func(i):
     5     time.sleep(random.uniform(0.1,0.9))
     6     print("当前线程号是{}".format(ct().ident))
     7     return "*" * i
     8         
     9         
    10 if __name__ == "__main__":
    11     t = ThreadPoolExecutor() # 20个线程
    12     
    13     # 通过线程池的map处理数据,返回迭代器
    14     it = t.map(func,range(100))
    15     print(isinstance(it , Iterator))
    16     
    17     # 协调子主线程的同步性,等所有线程池任务结束之后,在放行
    18     t.shutdown()
    19     
    20     for i in it:
    21         print(i)
    22     print("主线程执行结束 ... ")
    View Code

    5、总结(进程池、线程池)

    总结:无论是进程池还是线程池,都是由固定的进程数或者线程数完成的
    系统不会额外创建更多的进程或者线程完成任务;

    6、进程池 与 线程池 的 回调函数

    回调函数 : 回头调用一下函数
    微信支付付款成功: 通过回调函数获取金额
    微信支付退款状态: 通过回调函数获取金额状态

    add_done_callback:完成回调函数的调用
    add_done_callback作用:
        可以在获取result返回值时,变成异步并发程序;
        最后调用一下自定义的回调函数

    1)进程任务

    结论:进程池的回调函数由主进程完成

     1 from concurrent.futures import ProcessPoolExecutor , 
     2 import os,time,random
     3 
     4 def func1(i):
     5     time.sleep(random.uniform(0.1,0.9))
     6     print(i)
     7     print("进程任务执行中 .... 进程号{}".format(os.getpid()))
     8     print("进程任务结束了 .... 进程号{}".format(os.getpid()))
     9     return i
    10     
    11 def call_back1(obj):
    12     # 获取当前进程号
    13     print("<=====回调函数的进程号{}======>".format(os.getpid()))
    14     # 获取返回值
    15     print(obj.result())
    16 if __name__ == "__main__":
    17     # ### 进程池
    18     """***结论:进程池的回调函数由主进程完成"""
    19     p = ProcessPoolExecutor()
    20     for i in range(1,11):
    21         obj = p.submit(func1,i)
    22         # 1.异步获取当前进程的返回值
    23         obj.add_done_callback(call_back1)
    24         # 2.同步获取当前进程的返回值p
    25         # print(obj.result())
    26         p.shutdown()
    27     print("主线程执行结束 ... 线程号{}".format(os.getpid()os.getpid()))
    28     
    29 
    30 
    31 
    32 
    33 
    34     
    View Code

    2)线程任务

    结论:线程池的回调函数由对应线程池中的子线程完成

     1 from concurrent.futures import  ThreadPoolExecutor
     2 from threading import currentThread as ct
     3 import os,time,random
     4 # ### 线程任务
     5 def func2(i):
     6     time.sleep(random.uniform(0.1,0.9))
     7     print(i)
     8     print("线程任务执行中 .... 线程号{}".format(ct().ident))
     9     print("线程任务结束了 .... 线程号{}".format(ct().ident))
    10     return i
    11     
    12 def call_back2(obj):
    13     # 获取当前线程号
    14     print("<=====回调函数的线程号{}======>".format(ct().ident))
    15     # 获取返回值
    16     print(obj.result())
    17 if __name__ == "__main__":
    18         # ### 线程池
    19     """***结论:线程池的回调函数由对应线程池中的子线程完成"""
    20     t = ThreadPoolExecutor()
    21     for i in range(1,11):
    22         obj = t.submit(func2,i)
    23         # 1.异步获取当前线程的返回值
    24         obj.add_done_callback(call_back2)
    25         # 2.同步获取当前线程的返回值
    26         # print(obj.result())
    27 
    28     t.shutdown()
    29     print("主线程执行结束 ... 线程号{}".format(ct().ident))
    30     
    View Code

    3)模拟一下 add_done_callback 内部实现

     1 class Ceshi():
     2     def add_done_callback(self,fn):
     3         print("执行操作1")
     4         print("执行操作2")
     5         fn(self)
     6         
     7     def result(self):
     8         return "这是我的返回值"
     9         
    10 def call_back1(obj):
    11     print(obj.result())
    12     
    13 
    14 obj = Ceshi()
    15 obj.add_done_callback(call_back1)
    View Code

    四、协程

    总结: 协程是基于线程的一种异步并发程序;

    进程是资源分配的最小单位
    线程是程序调度的最小单位
    协程是线程实现的具体过程

    总结:
    在进程一定的情况下,开辟多个线程
    在线程一定的情况下,创建多个协程
    提高更大的并发,充分利用cpu

    1、 利用协程改写生产者消费者模型

    def producer():
        for i in  range(2000):
            yield i
    
    def consumer(gen):
        for i in range(10):
            print(  next(gen)  )
    
    # 初始化生成器函数 => 生成器对象(简称生成器)
    gen = producer()
    
    consumer(gen)
    consumer(gen)
    consumer(gen

    2、协程的终极形态 (猴子补丁)

    from gevent import monkey ; monkey.patch_all()
    # 猴子补丁可以把接下来引入的所有模块中的阻塞,自动识别出来;自动遇到阻塞就切换任务;
    import time
    import gevent
    
    def eat():
        print("吃吃吃1 ...")
        time.sleep(3)
        print("喝喝喝2 .... ")
    
    def play():
        print("玩玩玩1 ...")
        time.sleep(3)
        print("乐乐乐2 .... ")    
    
    g1 = gevent.spawn(eat)
    g2 = gevent.spawn(play)
    
    g1.join()
    g2.join()
    print("主线程执行结束")

    3、协程方法

    (1) spawn(函数,参数1,参数2,参数3 .. ) 创建协程
    (2) join 阻塞,直到某个协程任务执行完毕之后,放行;
     (3) value 获取协程任务中的返回值 g1.value g2.value
    (4) joinall 等待所有协程任务执行完毕之后,放行;
        g1.join()
        g2.join()
        => gevent.joinall( [g1,g2 .... ] )

     1 from gevent import monkey ; monkey.patch_all()
     2 import time
     3 import gevent
     4 
     5 def eat():
     6     print("吃吃吃1 ...")
     7     time.sleep(3)
     8     print("喝喝喝2 .... ")
     9     return "吃完了"
    10 
    11 def play():
    12     print("玩玩玩1 ...")
    13     time.sleep(3)
    14     print("乐乐乐2 .... ")    
    15     return "玩完了"
    16 
    17 g1 = gevent.spawn(eat)
    18 g2 = gevent.spawn(play)
    19 
    20 # 等待所有协程任务执行完毕之后,放行;
    21 gevent.joinall( [g1,g2] )
    22 print("主线程执行结束")
    23 
    24 # 获取协程任务中的返回值
    25 print(g1.value)
    26 print(g2.value)
    View Code

    4、协程案例

    http 状态码:
      200 ok
      400 bad request
      404 not found

     1 # 基本语法
     2 import requests
     3 # 返回响应对象(get获取时,必须要加http协议)
     4 response = requests.get("http://www.baidu.com/")
     5 print(response)
     6 # 获取状态码
     7 print(response.status_code)
     8 # 获取网页中字符编码
     9 res = response.apparent_encoding
    10 print(res)
    11 # 设置编码集,防止乱码
    12 response.encoding = res
    13 # 获取页面内容
    14 res = response.text
    15 print(res)
    爬虫基本语法
    (1) 正常爬取
     1 from gevent import monkey ; monkey.patch_all()
     2 import time
     3 import gevent
     4 import requests
     5 url_lst = [
     6     "http://www.baidu.com",
     7     "http://www.taobao.com/",
     8     "http://www.jd.com/",
     9     "http://www.4399.com/",
    10     "http://www.7k7k.com/",
    11     "http://www.baidu.com",
    12     "http://www.taobao.com/"]
    13 def get_url(url):
    14     response = requests.get(url)
    15     if response.status_code == 200:
    16         # print(response.text)
    17         pass
    18 startime = time.time()
    19 for i in url_lst:
    20     get_url(i)
    21 endtime = time.time()
    22 print("所用时间是{}".format(endtime - startime))
    View Code
    (2) 异步多协程的方式抓取页面
     1 from gevent import monkey ; monkey.patch_all()
     2 import time
     3 import gevent
     4 import requests
     5 url_lst = [
     6     "http://www.baidu.com",
     7     "http://www.taobao.com/",
     8     "http://www.jd.com/",
     9     "http://www.4399.com/",
    10     "http://www.7k7k.com/",
    11     "http://www.baidu.com",
    12     "http://www.taobao.com/",
    13     "http://www.jd.com/"]
    14 def get_url(url):
    15     response = requests.get(url)
    16     if response.status_code == 200:
    17         # print(response.text)
    18         pass
    19 
    20 lst = []
    21 startime = time.time()
    22 for i in url_lst:
    23     g = gevent.spawn(get_url , i)
    24     lst.append(g)
    25 
    26 # 必须等待所有协程任务执行完毕之后,计算最后的时间;
    27 gevent.joinall(lst)
    28 endtime = time.time()
    29 
    30 print("所用时间是{}".format(endtime - startime))
    View Code
  • 相关阅读:
    单向认证
    电商积分支付系统构建经验与总结
    python decimal.quantize()参数rounding的各参数解释与行为
    mysql 由decimal 引起的 Warning: Data truncated for column
    aliyun centos14.04 trusty 上安装docker1.12.1
    使用 py.test 对 python 代码进行测试
    mysql常用增删改查命令(纯纪录.orm用得基本功都没了。)
    python 协程库gevent学习--gevent数据结构及实战(四)
    http请求头中的content-type属性
    坚持做技术写作
  • 原文地址:https://www.cnblogs.com/yj0405/p/14224257.html
Copyright © 2020-2023  润新知