欢迎访问 生活随笔!

生活随笔

当前位置: 首页 >

异步同步、阻塞非阻塞、异步回调、线程队列和协程

发布时间:2025/7/14 47 豆豆
生活随笔 收集整理的这篇文章主要介绍了 异步同步、阻塞非阻塞、异步回调、线程队列和协程 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

  今天学习了异步同步、阻塞非阻塞、异步回调、线程队列和协程

  一、异步同步和阻塞非阻塞

  线程的三种状态:

    1、就绪

    2、运行

    3、阻塞

  阻塞:遇到了IO操作  代码卡住  无法执行下一行  CPU会切换到其他任务

  非阻塞: 与阻塞相反 代码正在执行(运行状态) 或处于就绪状态

  阻塞和非阻塞描述的是运行的状态

  同步:提交任务必须等待任务完成,才能执行下一行

  异步:提交任务不需要等待任务完成,立即执行下一行

  指的是一种提交任务的方式

  二、异步回调

    为什么回调:子进程帮助主进程完成任务 处理任务的结果应该交还给主进程
    其他方式也可以将数据交还给主进程

      1、shutdown  主进程会等到所有任务完成

      2、result函数  会阻塞直到任务完成

    注意:

      回调函数什么时候被执行?子进程完成时

      谁在执行回调函数?主进程

    线程的异步回调

      使用方式都相同  唯一的不同是执行回调函数 是子线程在执行

#进程利用回调完成生产者消费者
from
concurrent.futures import ProcessPoolExecutor import os pool = ProcessPoolExecutor()#爬虫 从网络某个地址获取一个HTML文件 import requests #该模块用于网络请求 #生产数据 def get_data_task(url):print(os.getpid(),'正在生产数据!')response = requests.get(url)text = response.content.decode('utf-8')return text#处理数据 def parser_data(f):print(os.getpid(),'处理数据')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') #线程利用回调完成生产者消费者 from concurrent.futures import ThreadPoolExecutor from threading import current_threadpool = ThreadPoolExecutor #爬虫 从网络某个地址获取一个HTML文件 import requests #该模块用于网络(HTTP)请求 #生产数据 def get_data_task(url):print(current_thread(),'正在生产数据!')response = requests.get(url)text = response.content.decode('utf-8')return text#处理数据 def parser_data(f):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')

  三、线程队列

  

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())

  四、协程

    协程的目的是在单线程下实现并发

    单线程下实现并发 将io阻塞时间用于执行计算 可以提高效率 原理:一直使用CPU直到超时

    怎么实现单线程并发?

    并发  指的是  看起来像是同时运行 实际是在任务间来回切换 同时需要保存执行的状态

    任务是一堆代码 可以用函数装起来

    1.如何让两个函数切换执行

      yield可以保存函数的执行状态

      通过生成器可以实现伪并发

      并发不一定提升效率  反而会降低效率 当任务全是计算时

    2.如何知道发生了io?从而切换执行

      目前咱们实现不了。。

    第三方模块 greenlet 可以实现并发 但是不能检测io

    第三方模块 gevent 封装greenlet 可以实现单线程并发 并且能够检测io操作 自动切换

    

#用yield实现两个函数切换执行 import time def task():while True:print('task1')time.sleep(4)yield 1def task2():g = task()while True:try:print('task2')next(g)except Exception:print('任务完成')break task2() #使用greenlet模块实现并发 import greenlet import time def task1():print('task1 1')time.sleep(2)g2.switch()print('task1 2')g2.swith()def task2():print('task2 1')g1.switch()print('task2 2')g1 = greenlet.greenlet(task1) g2 = greenlet.greenlte(task2) g1.switch() #1.实例化greenlet得到一个对象 传入要执行的任务 #2.先让某个任务执行起来 使用对象调用switch #3.在任务的执行过程中 手动调用switch来切换 #使用gevent模块实现单线程的并发 from gevent import monkey monkey.patch_all() import gevent import time def eat():print('eat food 1')time.sleep(2)print('eat food 2')def play():print('play 1')time.sleep(1)print('play 2')g1 = gevent.spawn(eat) g2 = gevent.spawn(play) gevent.joinall([g1,g2]) print('')#1.spawn函数传入你的任务 #2.调用join 去开启任务 #3.检测io操作需要打mokey补丁 就是一个函数 在程序最开始的地方调用它

 

转载于:https://www.cnblogs.com/xiaocaiyang/p/9954077.html

《新程序员》:云原生和全面数字化实践50位技术专家共同创作,文字、视频、音频交互阅读

总结

以上是生活随笔为你收集整理的异步同步、阻塞非阻塞、异步回调、线程队列和协程的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。