zoukankan      html  css  js  c++  java
  • python学习第33天

    一.死锁,互斥锁,递归锁

    死锁:只上锁不解锁容易造成死锁现象

    互斥锁:加一把锁就对应解一把锁,形成互斥锁

    递归锁:用于解决死锁,只是一种应急的处理方法

    from threading import RLock

    从语法上讲,锁可以互相嵌套,但不要使用

    不要因为逻辑问题让上锁分成两次,导致死锁

    二.线程队列

    from queue import Queue
    put 存
    get 取
    put_nowait 存,超出了队列长度,报错
    get_nowait 取,没数据取不出来,报错
    
    linux windows 线程中put_nowait,get_nowait都支持
    

    (1) Queue

    先进先出,后进后出

    (2)LifoQueue

    先进后出,后进先出(按照栈的特点设计)

    from queue import LifoQueue
    lq = LifoQueue(3)
    lq.put(11)
    lq.put(22)
    lq.put(33)
    # print(lq.put_nowait(444))
    
    print(lq.get())
    print(lq.get())
    print(lq.get())
    

    (3)PriorityQueue

    按照优先级顺序排序(默认从小到大排序)

    from queue import PriorityQueue
    
    # 1.如果都是数字,默认从小到大排序
    pq = PriorityQueue()
    pq.put(13)
    pq.put(3)
    pq.put(20)
    print(pq.get())
    print(pq.get())
    print(pq.get())
    
    # 2.如果都是字符串
    """如果是字符串,按照ascii编码排序"""
    pq1 = PriorityQueue()
    pq1.put("chinese")
    pq1.put("america")
    pq1.put("latinos")
    pq1.put("blackman")
    
    print(pq1.get())
    print(pq1.get())
    print(pq1.get())
    print(pq1.get())
    
    # 3.要么全是数字,要么全是字符串,不能混合 error
    """
    pq2 = PriorityQueue()
    pq2.put(13)
    pq2.put("aaa")
    pq2.put("拟稿")
    """
    
    pq3 = PriorityQueue()
    # 4.默认按照元组中的第一个元素排序
    pq3.put( (20,"wangwen") )
    pq3.put( (18,"wangzhen") )
    pq3.put( (30,"weiyilin") )
    pq3.put( (40,"xiechen") )
    
    print(pq3.get())
    print(pq3.get())
    print(pq3.get())
    print(pq3.get())
    

    三.进程池和线程池

    # 线程池
        # 实例化线程池      ThreadPoolExcutor    (推荐5*cpu_count)
        # 异步提交任务      submit / map
        # 阻塞直到任务完成   shutdown
        # 获取子线程的返回值 result
        # 使用回调函数      add_done_callback
     
    # 回调函数
        就是一个参数,将这个函数作为参数传到另一个函数里面.
        函数先执行,再执行当参数传递的这个函数,这个参数函数是回调函数
    
    # 线程池 是由子线程实现的
    # 进程池 是由主进程实现的    
    
    from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
    import os,time
    def func(i):
       print("任务执行中... start" , os.getpid())
       time.sleep(10)
       print("任务结束... end" , i)
       return i
    

    (1) ProcessPoolExecutor 进程池基本使用

    """默认如果一个进程短时间内可以完成更多的任务,就不会创建额外的新的进程,以节省资源"""
    if __name__ == "__main__":
       lst = []
       # print(os.cpu_count()) # 8 cpu逻辑核心数
       # (1) 创建进程池对象
       """进程池里面最多创建os.cpu_count()这么多个进程,所有任务全由这几个进程完成,不会额外创建进程"""
       p = ProcessPoolExecutor()
       
       # (2) 异步提交任务
       for i in range(10):
          res = p.submit(func,i)
          lst.append(res)       
    
       # (3) 获取当前进程池返回值
       # for i in lst:
          # print(i.result())
       
       # (4) 等待所有子进程执行结束
       p.shutdown()  # join   
       
       print("主程序执行结束....")
    

    (2) ThreadPoolExecutor 线程池的基本用法

    """默认如果一个线程短时间内可以完成更多的任务,就不会创建额外的新的线程,以节省资源"""
    from threading import current_thread as cthread
    def func(i):
       print("thread ... start" , cthread().ident,i)
       time.sleep(3)
       print("thread ... end" , i )   
       return cthread().ident
         
    if __name__ == "__main__":
       lst = []
       setvar = set()
       # (1) 创建线程池对象
       """限制线程池最多创建os.cpu_count() * 5 = 线程数,所有任务全由这几个线程完成,不会额外创建线程"""
       tp = ThreadPoolExecutor()# 我的电脑40个线程并发
       
       # (2) 异步提交任务
       for i in range(100):
          res = tp.submit(func,i)
          lst.append(res)
       
       # (3) 获取返回值
       for i in lst:
          setvar.add(i.result())
       
       # (4) 等待所有子线程执行结束
       tp.shutdown()
       
       print(len(setvar) , setvar)
       print("主线程执行结束 ... ")
    

    (3)线程池 map

    from concurrent.futures import ThreadPoolExecutor
    from threading import current_thread as cthread
    from collections import Iterator
    def func(i):
       # print("thread start ... ",cthread().ident)
       # print("thread end ... ",i)
       time.sleep(0.5)
       return "*" * i
    if __name__ == "__main__":
       setvar = set()
       lst = []
       tp = ThreadPoolExecutor(5)
       # map(自定义函数,可迭代性数据) 可迭代性数据(容器类型数据,迭代器,range对象)
       it = tp.map(func,range(20))
       # 判定返回值是否是迭代器
       print(isinstance(it,Iterator))
       
       tp.shutdown()  
       
       for i in it:   
          print(i)
    

    四.回调函数

    把函数当成参数传递给另外一个函数

    在当前函数执行完毕之后,最后调用一下该参数(函数),这个函数就是回调函数

    from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
    from threading import current_thread as cthread
    import os,time
    
    def func1(i):
       print("Process start ... ",os.getpid())
       time.sleep(0.5)
       print("Process end ... ",i)
       return "*" * i
    
    def func2(i):
       print("thread start ... ",cthread().ident)
       time.sleep(0.5)
       print("thread end ... ",i)
       return "*" * i
       
    def call_back1(obj):
       print("<==回调函数callback进程号:===>",os.getpid())
       print(obj.result())
    
    def call_back2(obj):
       print("<==回调函数callback线程号:===>",cthread().ident)
       print(obj.result())
    

    (1) 进程池的回调函数 : 由主进程执行调用完成

    if __name__ == "__main__":
       p = ProcessPoolExecutor(5)
       for i in range(1,11):
          res = p.submit(func1,i)
          # 进程对象.add_done_callback(回调函数) 
          '''
          add_done_callback 可以把res本对象和回调函数自动传递到函数里来
          '''
          res.add_done_callback(call_back1)
       p.shutdown()
       print("主进程执行结束 ... " , os.getpid())
    

    (2) 线程池的回调函数: 由当前子线程执行调用完成

    if __name__ == "__main__":
       tp = ThreadPoolExecutor(5)
       for i in range(1,11):
          res = tp.submit(func2,i)
          # 进程对象.add_done_callback(回调函数) 
          '''
          add_done_callback 可以把res本对象和回调函数自动传递到函数里来
          '''
          res.add_done_callback(call_back2)
       tp.shutdown()
       print("主线程执行结束 ... " , cthread().ident)
    

    (3) 回调函数原型

    # add_done_callback 原型
    class Ceshi():
       def add_done_callback(self,func):
          print("执行操作1 ... ")
          print("执行操作2 ... ")
          func(self)
          
       def result(self):
          return 123456
    
    def call_back3(obj):
       print(obj)
       print(obj.result())
       
    obj = Ceshi()
    obj.add_done_callback(call_back3)
    

    五.协程

    先安装 gevent模块

    #协程也叫纤程: 协程是线程的一种实现.
        指的是一条线程能够在多任务之间来回切换的一种实现.
        对于CPU、操作系统来说,协程并不存在.
        任务之间的切换会花费时间.
        目前电脑配置一般线程开到200会阻塞卡顿.
        
    #协程的实现
    协程帮助你记住哪个任务执行到哪个位置上了,并且实现安全的切换
    一个任务一旦阻塞卡顿,立刻切换到另一个任务继续执行,保证线程总是忙碌的,更加充分的利用CPU,抢占更多的时间片
    # 一个线程可以由多个协程来实现,协程之间不会产生数据安全问题
    
    #协程模块
        # greenlet  gevent的底层,协程,切换的模块
        # gevent    直接用的,gevent能提供更全面的功能
    
    

    (1) 协程的具体实现

    switch 遇到阻塞时,只能手动调用该函数进行函数切换,不能自动实现切换,来规避io阻塞;
    
    from greenlet import greenlet
    import time
    
    def eat():
       print("eat 1")
       g2.switch()
       time.sleep(3)
       print("eat 2")
       
    def play():
       print("play one")  
       time.sleep(3)
       print("play two")
       g1.switch()
       
    g1 = greenlet(eat)
    g2 = greenlet(play)
    g1.switch()
    

    (3) gevent

    gevent 可以自动切换,但是不能够自动识别time.sleep这样的阻塞
    
    import gevent
    def eat():
       print("eat 1")
       time.sleep(3)
       print("eat 2")
       
    def play():
       print("play one")  
       time.sleep(3)
       print("play two")
    
    # 利用gevent.spawn创建协程对象g1
    g1 = gevent.spawn(eat)
    # 利用gevent.spawn创建协程对象g2
    g2 = gevent.spawn(play)
    
    # 阻塞,必须g1协程执行完毕为止
    g1.join()  
    # 阻塞,必须gg协程执行完毕为止
    g2.join()
    
    print("主线程执行完毕 ... ")
    

    (4) gevent,time 添加阻塞,让他实现自动切换

    def eat():
       print("eat 1")
       gevent.sleep(3)
       print("eat 2")
       
    def play():
       print("play one")  
       gevent.sleep(3)
       print("play two")
    
    # 利用gevent.spawn创建协程对象g1
    g1 = gevent.spawn(eat)
    # 利用gevent.spawn创建协程对象g2
    g2 = gevent.spawn(play)
    
    # 阻塞,必须g1协程执行完毕为止
    g1.join()  
    # 阻塞,必须gg协程执行完毕为止
    g2.join()
    print("主线程执行完毕 ... ")
    

    (5) 终极大招 彻底解决不识别阻塞的问题

    from gevent import monkey
    monkey.patch_all() # 把下面所有引入的模块中的阻塞识别一下
    import time
    import gevent 
    
    def eat():
       print("eat 1")
       time.sleep(3)
       print("eat 2")
       
    def play():
       print("play one")  
       time.sleep(3)
       print("play two")
       
    # 利用gevent.spawn创建协程对象g1
    g1 = gevent.spawn(eat)
    # 利用gevent.spawn创建协程对象g2
    g2 = gevent.spawn(play)
    
    # 阻塞,必须g1协程执行完毕为止
    g1.join()  
    # 阻塞,必须gg协程执行完毕为止
    g2.join()
    print("主线程执行完毕 ... ")
    

    5 .1协程的例子

    (1) spawn(函数,参数1,参数2,参数3 .... )  启动协程
    (2) join 阻塞,直到某个协程任务执行完毕之后,再放行
    (3) joinall 等待所有协程任务都执行完毕之后,在放行
         g1.join()  g2.join()   <=>  gevent.joinall( [g1,g2] )(推荐:比较简洁)
    (4) value 获取协程任务中的返回值 g1.value g2.value 获取对应协程中的返回值 
    

    (1) 利用协程爬取数据

    requests 抓取页面数据模块
    
    HTTP 状态码
       200 ok
       404 not found
       400 bad request
    

    (2) 基本语法

    from gevent import monkey ; monkey.patch_all()
    import time
    import gevent
    import requests
    """
    response = requests.get("http://www.baidu.com")
    print(response)
    # 获取状态码
    print( response.status_code )
    # 获取网页中的字符编码
    res = response.apparent_encoding
    print( res )
    # 设置编码集,防止乱码
    response.encoding = res
    # 获取网页当中的内容
    res = response.text
    print(res)
    
    def get_url(url):
       response = requests.get(url)
       if response.status_code == 200:
          # print(response.text)
          time.sleep(0.1)
    

    (3) 正常爬取

    starttime = time.time()
    for i in url_list:
       get_url(i)
    endtime = time.time()
    print("执行时间:" ,endtime - starttime ) 
    
    import re
    strvar = '<img lz_src="http://i5.7k7kimg.cn/cms/cms10/20200609/113159_2868.jpg"'
    obj = re.search(r'<img lz_src="(.*?)"',strvar)
    print(obj.groups()[0])
    

    (4) 用协程的方式爬取数据

    lst = []
    starttime = time.time()
    for i in url_list:
       g = gevent.spawn(get_url,i)
       lst.append(g)
    
    gevent.joinall(lst)    
    
    endtime = time.time()
    
    print("执行时间:" ,endtime - starttime ) # 执行时间: 2.3307271003723145
    

    利用多进程,多线程,多携程可以让服务器运行速度更快

    并且也可以抗住更多用户的访问

  • 相关阅读:
    MySQL数据库to_char() to_date()
    爬虫的基本要求和考核标准
    打印MySQL操作错误
    暖通施工时遇到大梁不需要打洞,开小孔的三通时无需放样
    CAD 图纸上的实体的扩展数据不能记数据库中的ID 因为数据库数据删除时,ID也被删除,这样就找不到该实体的信息了
    关于定时关机的命令 shutdown s t 30 at 12:00 shutdown s
    下次坐飞机时,我要揣个弹弓进去, 因为我要去完成我小时候想完成而没能完成的梦想
    CAD 块与块相交,交点在块的边界上,不在块内的某实体上
    豆奶粉干吃好吃
    CAD 我们用弧度时 不要去定义Pi是多少,用Math.Pi
  • 原文地址:https://www.cnblogs.com/yunchao-520/p/13121928.html
Copyright © 2011-2022 走看看