• 2019.9.19学习内容及小结


    小结
    上节课回顾

    '''
    线程锁(Lock):本质就是互斥锁(*****)
        使用目的:保证数据安全
    死锁问题:线程1抢到了锁A没释放
              线程2抢到了锁B没释放
              线程1往下执行需要锁B
              线程2往下执行需要锁A
            解决方案:递归锁(RLock)
            特点:只有同一个线程下可以多次acquire,acquire了几次就要release几次
    
    信号量:
           实现:定制了锁的个数,也就意味着最多有几个线程可以抢到锁头,这几个线程就可以实现并发
    
    GIL(****)
    什么是GIL:
    Cpython中有GIL锁(全局解释器锁),GIL锁本质就是一把互斥锁,
      GIL锁导致了 python 的同一个进程同一时刻下只有一个线程在执行代码
    为什么要有?
    因为Cpython自带的垃圾回收机制不是线程安全的,所以要有GIL锁
    
    多进程vs多线程(*****)
    计算密集型任务===》推荐使用多进程解决===》要利用多核优势,并行的去计算
    io密集型任务===》推荐使用多线程解决===》大部分时间都在io,并且开启一个线程比开启一个进程速度要快的多
    大部分的需求都是io密集型,因为大部分的软件都是基于网络的(存在网络io)
    
    cpu主要负责计算
    
    
    ########### 今日内容 ###########
    线程queue 线程定时器 (*)
    线程queue三种用法
    1.先进先出
    2.先进后出
    3.优先级    优先级判断  通常用数字表示  数字小的先出
    
    多线程并发socket服务端(*****)
      具体看代码,要看明白
    
    进程池,线程池(****)
      必须把进程池、线程池的概念搞懂
      能看懂代码
    
    协程:
    
    
    
    '''
    

    线程queue

    ###### 先进先出 ######
    # import queue
    # q = queue.Queue() #拿到线程queue 对象
    # q.put('123') # 往queue里放数据
    # q.put('456')
    # q.put('789')
    #
    # print(q.get()) #123  #取出 queue里面的数据(这是先进先出)
    # print(q.get()) #456
    # print(q.get()) #789
    # q.task_done()
    # q.task_done()
    # q.task_done()
    # q.join()
    '''
    task_done() 和 join()
    每次从queue中get一个数据之后,当处理好相关问题,最后调用该方法,
    以提示q.join()是否停止阻塞,让线程向前执行或者退出;
    q.join(),阻塞,直到queue中的数据均被删除或者处理。为队列中的每一项都调用一次
    如:上述例子,如果put了三次(相当于放了三个值),而只get了两次(取
    出了两个值),然后用了两次task_done(),那么最后的join()会阻塞,
    知道把队列里的数据全部处理完后才停止阻塞
    '''
    
    ######## 堆栈 先进后出 #########
    # import queue
    # q = queue.LifoQueue() #堆栈   先进后出
    # q.put('123')
    # q.put('456')
    # q.put('789')
    #
    # print(q.get())#789
    # print(q.get())#456
    # print(q.get())#123
    
    
    ######## 优先级 ########
    import queue
    q = queue.PriorityQueue() #可以根据优先级取数据
    # 通常这个元组的第一个值是int类型 数小的先出
    q.put((50, '123'))
    q.put((60, '456'))
    q.put((10, '789'))
    
    print(q.get()) #(10, '789')
    print(q.get()) #(50, '123')
    print(q.get()) #(60, '456')
    

    线程定时器

    from threading import Timer
    import time
    
    def task():
        print('线程执行了')
        time.sleep(2)
        print('线程结束了')
    
    t = Timer(5, task) #定时5s后开启线程,不会阻塞下面的代码
    t.start()
    print('123123') #正常执行,123123
    

    进程池和线程池

    '''
    进程池线程池:
        池的功能限制进程数或线程数。
        什么时候需要限制?
        当并发的任务数量远远大于计算机所能承受的范围,
        即无法一次性开启过多的任务数量,就应该考虑去限制我进程数或者线程数,
        从而保证服务器不蹦
    '''
    #1.导入进程池或线程池模块
    from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
    from threading import currentThread
    from multiprocessing import current_process
    import time
    # 2.构造一个工作
    def task(i):
        #print(f'{currentThread().name} 在执行 {i}')
        print(f'{current_process().name} 在执行 {i}')
        time.sleep(1)
        return i**2
    
    if __name__ == '__main__':
        #pool = ThreadPoolExecutor(4) # 池子里只有4个线程
        #3.定制池中进程或线程 数量
        pool = ProcessPoolExecutor(4) #池子里只有4个进程
        fu_list = []
        # 首先考虑开启任务数量(开启任务较多考虑使用进程池线程池)
        for i in range(20):
            #pool.submit(task, i) #提交池,把要处理的函数名及其它参数放进去
            #4. 提交池,传相应参数
            future = pool.submit(task, i) #task任务要做20次,4个进程负责做这个事
            #print(future.result()) #如果没有结果一直等待拿到结果,这样导致了所有任务都在串行
            fu_list.append(future)
        #5.关闭池
        pool.shutdown() #关闭了池入口,会等待所有的任务执行完,结束阻塞
        for fu in fu_list:
            #6.打印结果
            print(fu.result())
    

    进程池和线程池02

    '''
    进程池线程池:
            池的功能限制进程数或线程数
            什么时候限制?
            当并发的任务数量远远大于计算机所能承受的范围,即无法一次性开启过多的任务数量
            这时就应该考虑去限制开启的进程数或线程数,从而保证服务器不崩
    
    理解为提交任务的两种方式
    同步:提交了一个任务,必须等任务执行完了(拿到返回值),才能执行下一行代码
    
    异步:提交了一个任务,,不要等执行完了,可以直接执行下一行代码
    
    '''
    
    from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
    from threading import currentThread
    from multiprocessing import current_process
    import time
    
    def task1(i):
        #print(f'{currentThread().name} 在执行 {i}')
        print(f'{current_process().name} 在执行 {i}')
        time.sleep(1)
        return i**2
    
    def parse(future):
        # 处理拿到的结果
        print(future.result())
    
    if __name__ == '__main__':
        #pool = ThreadPoolExecutor(4)
        pool = ProcessPoolExecutor(4)
        for i in range(20):
            future = pool.submit(task1, i)
            future.add_done_callback(parse)
            # 为当前任务绑定一个函数,在当前任务执行结束的时候会触发这个函数
            #会把future对象作为参数传函数
            #这个称之为回调函数,处理完了回来就调用这个函数
    

    协程

    '''
    python的 线程用的是操作系统 原生的线程
    
    协程:单线程下实现并发
         并发:切换+保存状态
         多线程:操作系统帮你实现的(同一进程同一时间只能运行一个线程),如果遇到io会被切换,执行时间过长也会切换,实现一个雨露均沾的效果
    
         什么样的协程是有意义的?
            遇到io切换的时候才有意义
            具体:
            协程概念本质是程序员抽象出来的,操作系统根本不知道协程的存在,
            也就是说来了一个线程我自己遇到io,我自己线程内部直接切到自己
            的别的任务上,操作系统根本发现不了,也就是实现了单线程下效率最高
    
         优点:
            自己控制切换要比操作系统切换快的多
         缺点:
            对比多线程
            自己要检测所有的io,但凡有一个阻塞整体都要跟着阻塞
    
            对比多进程
            无法利用多核优势
    为什么要有协程(遇到io切换 )?
         自己控制切换要比操作系统切换快的多,降低了单个线程的io时间
    
    '''
    
    # import time
    # def eat():
    #     print('eat 1')
    #     # 疯狂的计算没有io
    #     time.sleep(2)
    #
    # def play():
    #     print('play 1')
    #     # 疯狂的计算没有io
    #     time.sleep(3)
    
    # play()
    # eat() #5s
    
    # import time
    # def func():
    #     while True:
    #         1000000+1
    #         yield
    # def func2():
    #     g = func()
    #     for i in range(100000000):
    #         a=i + 1
    #         next(g)
    #         print(a)
    # start = time.time()
    # func2()
    # end = time.time()
    # print(end - start) #24.284388780593872
    
    import time
    def func():
        for i in range(100000000):
            i + 1
    def func2():
        for i in range(100000000):
            i + 1
    start= time.time()
    func()
    func2()
    end= time.time()
    print(end - start) #16.235928535461426
    
    '''对比:通过yeild切换运行的时间反而比串行 更消耗时间,这样实现的协程是没有意义的'''
    

    协程02

    from gevent import monkey
    monkey.patch_all() #打了一个补丁,可以实现捕获非gevent的io
    import gevent
    import time
    
    def eat():
        print('eat 1')
        time.sleep(2)
        print('eat 2')
    def play():
        print('play 1')
        time.sleep(3)
        print('play 2')
    
    start = time.time()
    g1 = gevent.spawn(eat)
    g2 = gevent.spawn(play)
    g1.join()
    g2.join()
    end = time.time()
    print(end - start) #3.00417160987854
    
    ### 利用gvent模块在捕获非gvent的io,实现单线程并发,提高效率
    
  • 相关阅读:
    activemq 高可用集群部署
    rabbitmq单机部署、集群部署、haproxy+keepalived 的高可用负载均衡环境搭建
    redis 单机部署、集群部署(主从同步+哨兵)
    zookeeper 单机部署、伪集群部署、集群部署
    IDEA 中调试 dubbo 出现 <dubbo:reference interface="" /> interface not allow null! 异常
    centos 安装 subversion1.8及更高版本
    mysql 两主一从环境搭建
    mysql 一主多从环境搭建
    springboot + post 中文乱码
    android:inputType参数类型说明
  • 原文地址:https://www.cnblogs.com/chmily/p/11557159.html
Copyright © 2020-2023  润新知