zoukankan      html  css  js  c++  java
  • 2018.11.13


    1同步异步,阻塞与非阻塞
    线程的三种状态:
    1.就绪
    2.运行
    3.阻塞
    #1.1、阻塞与非阻塞指的是程序的两种运行状态
    # 阻塞:遇到IO就发生阻塞,程序一旦遇到阻塞操作就会停在原地,并且立刻释放CPU资源
    # 非阻塞(就绪态或运行态):没有遇到IO操作,或者通过某种手段让程序即便是遇到IO操作也不会停在原地,执行其他操作,力求尽可能多的占有CPU


    #1.2、同步与异步指的是提交任务的两种方式:
    # 同步调用:提交完任务后,就在原地等待,直到任务运行完毕后,拿到任务的返回值,才继续执行下一行代码
    比如掉用一个普通的函数,然后拿到这个函数的return的返回值再进行下一步操作.这属于同步调用

    from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
    import time,os,random

    def task(x):
    print('%s 接客' %x)
    time.sleep(random.randint(1,3))
    return x**2

    if __name__ == '__main__':
    # 异步调用
    # p=ThreadPoolExecutor(4) # 默认开启的线程数是cpu的核数*5
    #
    # # alex,武佩奇,杨里,吴晨芋,张三
    #
    # obj_l=[]
    # for i in range(10):
    # obj=p.submit(task,i)
    # obj_l.append(obj)
    #
    # # p.close()
    # # p.join()
    # p.shutdown(wait=True)
    #
    # print(obj_l[3].result())
    # print('主')


    # 同步调用
    p=ThreadPoolExecutor(4) # 默认开启的线程数是cpu的核数*5

    # alex,武佩奇,杨里,吴晨芋,张三

    for i in range(10):
    res=p.submit(task,i).result()#等结果进行下一步

    print('主')

    2.异步回调 ******
    为什么需要回调?
    子进程帮助主进程完成任务 处理任务的结果应该交还给准进程
    add_done_callback
    其他方式也可以将数据交还给主进程
    1.shutdown 主进程会等到所有任务完成
    2.result函数 会阻塞直到任务完成
    都会阻塞 导致效率降低 所以使用回调
    注意:
    回调函数什么时候被执行? 子进程任务完成时
    谁在执行回调函数? 主进程
    线程的异步回调
    使用方式都相同 唯一的不同是执行回调函数 是子线程在执行

    from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
    import os, time


    pool = ProcessPoolExecutor()


    def task():
    print("%s is 正在打水" % os.getpid())
    # time.sleep(0.2)
    w = "%s 打的水" % os.getpid()
    return w


    def task_finish(res):
    print("打水完成! %s" % res)


    if __name__ == '__main__':
    for i in range(20):
    # 提交任务会返回一个对象 用于回去执行状态和结果
    f = pool.submit(task)
    f.add_done_callback(task_finish) # 添加完成后的回调
    # print(f.result())

    print("11111")
    pool.shutdown() # 首先不允许提交新任务 然后等目前所有任务完成后
    print("over")

    2.1.利用回调完成生产者消费者
    from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
    from threading import current_thread
    import os
    pool = ThreadPoolExecutor()


    import requests # 该模块用于网络(HTTP)请求

    # 生产数据
    def get_data_task(url):
    print(os.getpid(),"正在生产数据!")
    # print(current_thread(),"正在生产数据!")

    response = requests.get(url)
    text = response.content.decode("utf-8")
    print(text)
    return text


    # 处理数据
    def parser_data(f):
    print(os.getpid(),"处理数据")
    # print(current_thread(), "处理数据")
    print("正在解析: 长度%s" % len(f.result()))


    urls = [
    "http://www.baidu.com",
    "http://www.baidu.com",
    "http://www.baidu.com",
    "http://www.baidu.com"
    ]

    if __name__ == '__main__':
    for url in urls:
    f = pool.submit(get_data_task,url)
    f.add_done_callback(parser_data) # 回调函数是主进程在执行
    # 因为子进程是负责获取数据的 然而数据怎么处理 子进程并不知道 应该把数据还给主进程
    print("over")

    33.线程队列 *** 队列 堆栈优先级队列
    import queue

    # 普通队列 先进先出
    q = queue.Queue()
    q.put("a")
    q.put("b")


    print(q.get())
    print(q.get())

    # 堆栈队列 先进后出 后进先出 函数调用就是进栈 函数结束就出栈 递归造成栈溢出
    q2 = queue.LifoQueue()
    q2.put("a")
    q2.put("b")
    print(q2.get())


    # 优先级队列
    q3 = queue.PriorityQueue() # 数值越小优先级越高 优先级相同时 比较大小 小的先取
    q3.put((-100,"c"))
    q3.put((1,"a"))
    q3.put((100,"b"))
    print(q3.get())

    3协程 ** 2个模块greenlet gevent
    协程的目的是在单线程下实现并发
    为什么出现协程? 因为cpython 由于GIL 导致同一时间只有一个线程再跑
    意味着 如果你的程序时计算密集 多线程效率也不会提升
    如果是io密集型 有没有必要再单线程下实现并发
    没有 我会开启多线程来处理io 子线遇到io cpu切走 但是请问 你能保证一定切到主线吗? 不能保证
    有 如果可以 我在遇到io的时候转而去做计算 这样一来可以保证cpu一直在处理你的程序 当然时间太长也要切走

    总结一下:单线下实现并发 将io阻塞时间用于执行计算 可以提高效率 原理:一直使用CPU直到超时
    怎么实现单线程并发?
    并发 指的是 看起来像是同时运行 实际是在任务间来回切换 同时需要保存执行的状态
    任务一堆代码 可以用函数装起来
    1.如何让两个函数切换执行
    yield可以保存函数的执行状态
    通过生成器可以实现伪并发
    并发不一定提升效率 反而会降低效率 当任务全是计算时
    2.如何知道发生了io? 从而切换执行
    目前咱们实现不了
    第三方模块 greenlet 可以实现并发 但是不能检测io
    第三方模块 gevent 封装greenlet 可以实现单线程并发 并且能够检测io操作 自动切换

    协程的应用场景:
    TCP 多客户端实现方式
    1.来一个客户端就来一个进程 资源消耗较大
    2.来一个客户端就来一个线程 也不能无限开
    3.用进程池 或 线程池 还是一个线程或进程只能维护一个连接
    4.协程 一个线程就可以处理多个客户端 遇到io就切到另一个

    3,1greenlet使用
    import greenlet

    import time
    def task1():
    print("task1 1")
    time.sleep(2)
    g2.switch()
    print("task1 2")
    g2.switch()

    def task2():
    print("task2 1")
    g1.switch()
    print("task2 2")

    g1 = greenlet.greenlet(task1)
    g2 = greenlet.greenlet(task2)

    g1.switch()

    # 1.实例化greenlet得到一个对象 传入要执行的任务
    # 至少需要两个任务
    # 2.先让某个任务执行起来 使用对象调用switch
    # 3.在任务的执行过程中 手动调用switch来切换
    #

    3.2gevent使用
    from gevent import monkey
    monkey.patch_all()

    import gevent
    import time
    def eat():
    print('eat food 1')
    time.sleep(2)
    # gevent.sleep(1)
    print('eat food 2')

    def play():
    print('play 1')
    time.sleep(1)
    # gevent.sleep(1)
    print('play 2')

    g1=gevent.spawn(eat)
    g2=gevent.spawn(play)
    # g1.join()
    # g2.join()

    gevent.joinall([g1,g2])
    print('主')

    # 1.spawn函数传入你的任务
    # 2.调用join 去开启任务
    # 3.检测io操作需要打monkey补丁 就是一个函数 在程序最开始的地方调用它

  • 相关阅读:
    VC 中 C2275问题解决
    MIPS指令学习
    《高效人士的116个IT秘诀》读书笔记
    Mercurial入门学习
    foobar 插件安装
    五笔输入法的学习记录
    AutoHotKey入门使用
    windows shell
    CSPS 2021霜降记
    ubunru下jdk安装
  • 原文地址:https://www.cnblogs.com/jutao/p/9954365.html
Copyright © 2011-2022 走看看