同步异步 + 回调函数

重点记忆 异步回调函数

如果进程池+回调: 回调函数由主进程去执行.
如果线程池+回调: 回到函数由空闲的线程去执行.(比如有4个线程,10个任务,第一轮完成4个任务,交由主线程处理结果,第二轮同样如此,但是第三轮将会空闲出2个子进程,则这2个子进程将会和主进程一同处理结果,以此类推,当所有的任务完成时,所有的子进程和主进程一起处理结果,增加效率)

回调函数不管有没有返回数据,返回值都是None,回调函数内部加函数名是调用此函数,obj隐形传参

1.概念

1. 从执行的角度

  1. 阻塞: 程序运行时,遇到了io,程序挂起,操作系统强行将CPU切走
  2. 非阻塞: 程序员没有遇到io,或者程序员遇到io但是我通过某种手段,让CPU强行运行我的程序

2. 提交任务的角度

  1. 同步: 提交一个任务,子任务开始运行直到此任务结束(可能遇到 io ),并返回一个返回值,我在提交下一个任务
  2. 异步: 一次提交所有任务,然后我就直接执行下一行代码,效率高

3. 什么叫爬虫?

  1. 利用代码模拟一个浏览器,进行浏览器的工作流程得到一堆源代码.
  2. 对源代码进行数据清洗得到我想要数据.

2. 同步调用

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

def task(i):
    print(f'{os.getpid()}开始执行')
    time.sleep(random.randint(1,3))
    print(f'\033[1;35;0m{os.getpid()}任务结束\033[0m')
    return i  #就是obj.result()的返回值

if __name__ == '__main__':
    pool = ProcessPoolExecutor(4)
    for i in range(10):
        obj = pool.submit(task,i) #默认接受
        # obj是一个动态对象,返回的当前的对象的状态,有可能运行中,可能(就绪阻塞),还可能是结束了
        print(f'任务结果是:{obj}')
        print(f'任务结果是:{obj.result()}')
        #obj.result() #阻塞,必须等到这个任务完成并返回了结果之后,再执行下一个任务
    pool.shutdown(wait=True)
    # shutdown: 让我的主进程等待进程池中所有的子进程都结束任务之后, 再执行.有点类似与join
    print('==主')
obj是一个动态对象,返回的当前的对象的状态,有可能运行中,可能(就绪阻塞),还可能      是结束了.
obj.result() 必须等到这个任务完成后,返回了结果之后,在执行下一个任务.
shutdown: 让我的主进程等待进程池中所有的子进程都结束任务之后,再执行. 有点类    似与join.
shutdown: 在上一个进程池没有完成所有的任务之前,不允许添加新的任务.
一个任务是通过一个函数实现的,任务完成了他的返回值就是函数的返回值.

3.异步调用

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

def task(i):
    print(f'{os.getpid()}开始任务')
    time.sleep(random.randint(1,3))
    print(f'\033[1;35;0m{os.getpid()}任务结束\033[0m')
    return i

if __name__ == '__main__':
    #异步调用:基于发布任务的角度
    pool = ProcessPoolExecutor(4) #设置进程数,pid一共就四个
    for i in range(10): #一次发布10个任务
        pool.submit(task,i)
    pool.shutdown(wait=True)

4.爬虫简解

1.安装第三方模块 requests (安装方法见博客园)

import requests
ret = requests.get('http://www.taobao.com')
if ret.status_code == 200:  #固定写法,验证请求
    print(ret.text) #获取源码
    print(len(ret.text))  #有时候直接打印ret.text报错,是浏览器编码问题,代码可用

2.版本一

    1. 异步发出10个任务,并发的执行,但是统一的接收所有的任务的返回值.(效率低,不能实时的获取结果)
    2. 分析结果流程是串行,影响效率.
         for res in obj_list:
            print(parse(res.result()))
from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
import requests

def task(url):
    """模拟的就是多个源代码,一定有io操作(爬取时网络延迟)"""
    ret = requests.get(url)
    if ret.status_code == 200:
        return ret.text  #源码

def parse(content):
    """模拟对数据进行分析 一般没有IO"""
    return len(content)

if __name__ == '__main__':
    url_list = ['http://www.taobao.com',
                'http://www.baidu.com',
                'http://www.aqiyi.com',
                'http://www.youku.com',
                'https://gitee.com/',
                'https://www.cnblogs.com/jin-xin/articles/10067177.html']
    pool = ProcessPoolExecutor(1)
    obj_list = []
    for url in url_list:
        obj = pool.submit(task,url) #注意返回值不是ret.text
        obj_list.append(obj)
    print(obj_list)
    pool.shutdown(wait=True)
    for res in obj_list:
        print(parse(res.result()))  #res.result = ret.text  #分析结果串行
    print('==主')

3.版本二

  线程池设置4个线程, 异步发起10个任务,每个任务是通过网页获取源码+数据分析, 并发执行,最后将所有的结果展示出来.
  耦合性增强了.
  并发执行任务,此任务最好是IO阻塞,才能发挥最大的效果
from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
import requests

def task(url):
      """模拟的就是多个源代码,一定有io操作(爬取时网络延迟)"""
    ret = requests.get(url)
    if ret.status_code == 200:
        return parse(ret.text)

def parse(content):
    """模拟对数据进行分析 一般没有IO"""
    return len(content)

if __name__ == '__main__':
    url_list = [
            'http://www.baidu.com',
            'http://www.JD.com',
            'http://www.JD.com',
            'http://www.JD.com',
            'http://www.taobao.com',
            'https://www.cnblogs.com/jin-xin/articles/7459977.html',
            'https://www.luffycity.com/',
            'https://www.cnblogs.com/jin-xin/articles/9811379.html',
            'https://www.cnblogs.com/jin-xin/articles/11245654.html',
            'https://www.sina.com.cn/',
        ]
    pool = ProcessPoolExecutor(4)
    obj_list = []
    for url in url_list:
        obj = pool.submit(task,url)  #obj接收的是submit的返回值,一个动态对象
        obj_list.append(obj)
    pool.shutdown(wait=True)
    for res in obj_list:
        print(res.result())  #相当于obj.result()接受的是task的返回值

4.版本3 异步调用 + 回调函数

  1. 基于 异步调用回收所有任务的结果我要做到实时回收结果,# 并发执行任务每个任务只是处理IO阻塞的,不能增加新得功能.
  2. 重点
  3. 如果进程池+回调: 回调函数由主进程去执行.
    如果线程池+回调: 回到函数由空闲的线程去执行.
from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
import requests

def task(url):
    '''模拟的就是爬取多个源代码 一定有IO操作'''
    ret = requests.get(url)
    if ret.status_code == 200:
        return ret.text

def parse(self): #隐形传参,默认接受obj
    '''模拟对数据进行分析 一般没有IO'''
    print(len(self.result()))

if __name__ == '__main__':
    url_list = [
            'http://www.baidu.com',
            'http://www.JD.com',
            'http://www.JD.com',
            'http://www.JD.com',
            'http://www.taobao.com',
            'https://www.cnblogs.com/jin-xin/articles/7459977.html',
            'https://www.luffycity.com/',
            'https://www.cnblogs.com/jin-xin/articles/9811379.html',
            'https://www.cnblogs.com/jin-xin/articles/11245654.html',
            'https://www.sina.com.cn/',
        ]
    pool = ThreadPoolExecutor(4)

    for url in url_list:
        obj = pool.submit(task,url)
        obj.add_done_callback(parse)
        #回调函数不管有没有返回值,都是None,回调函数内部加函数名是调用此函数,obj隐形传参

原文地址:https://www.cnblogs.com/lvweihe/p/11415215.html

时间: 2024-10-11 06:39:41

同步异步 + 回调函数的相关文章

协程,事件,队列,同步,异步,回调函数

协程 什么是协成?单个线程并发的处理多个任务,程序控制协成的切换+保持状态,协成的切换速度非常快,蒙蔽了操作系统的眼睛,让操作系统认为CPU一直在运行 进程或线程都是由操作系统控制CPU来回切换,遇到阻塞就切换执行其他任务,协成是程序控制的,霸占CPU执行任务,会在操作系统控制CPU之前来回切换,操作系统就认为CPU一直在运作 协程的优点: 1.开销小 2.运行速度快 3.协程会长期霸占CPU只执行我程序里的所有任务 协程的缺点: 1.协程属于微并发,处理任务不易过多 2.协程的本质是单线程,无

python 管道 事件 信号量 进程池(map/同步/异步)回调函数

####################总结######################## 管道:是进程间通信的第二种方式,但是不推荐使用,因为管道会导致数据不安全的情况出现 事件:当我运行主进程的时候 需要子执行某个进程后 需要的返回值时 可以使用 信号量:互斥锁同时只允许一个线程更改数据,而信号量Semaphore是同时允许一定数量的线程更改数据 . 内部维护了一个计数器,acquire-1,release+1,为0的时候,其他的进程都要在acquire之前等待 进程池:  进程的创建和销

2015/01/23 – 不要对异步回调函数进行同步调用

绝对不能对异步回调函数(即使在数据已经就绪)进行同步调用. 如果对异步回调函数进行同步调用的话,处理顺序可能会与预期不符,可能带来意料之外的后果. 对异步回调函数进行同步调用,还可能导致栈溢出或异常处理错乱等问题. 如果想在将来某时刻调用异步回调函数的话,可以使用 setTimeout 等异步API. function onReady(fn) { var readyState = document.readyState; if (readyState == 'interactive' || re

js同步-异步-回调

出处:https://blog.csdn.net/u010297791/article/details/71158212(1)上面主要讲了同步和回调执行顺序的问题,接着我就举一个包含同步.异步.回调的例子. let a = new Promise(//声明了一个Promise回调函数,能够使用then function(resolve, reject) { console.log(1) setTimeout(() => console.log(2), 0) console.log(3) cons

同步回调函数和异步回调函数

回调函数 回调函数一般是在封装接口的时候,回调显得特别重要,我们首先假设有两个程序员在写代码,A程序员写底层驱动接口,B程序员写上层应用程序,然而此时底层驱动接口A有一个数据d需要传输给B,此时有两种方式: 1.A将数据d存储好放在接口函数中,B自己想什么时候去读就什么时候去读,这就是我们经常使用的函数调用,此时主动权是B. 2.A实现回调机制,当数据变化的时候才将通知B,你可以来读取数据了,然后B在用户层的回调函数中读取速度d,完成OK.此时主动权是A. 很明显第一种方法太低效了,B根本就不知

C#的异步回调函数

关于C#的异步回调,在ActionScript 3.0当中 , 有关键字Function , 可以直接传class函数为回调函数.但是在C#当中,需要使用到委托,其中应用的最多的当属 : Action / Func .当然你可以使用关键字delegate来自定义委托.这里翼Action /  Func为例来讲解C#的异步回调,如果不了解C#的委托机制,需要自己先百度/Google一下,再来看这篇博客. BeginInvoke 方法用于启动异步调用.它与您需要异步执行的方法具有相同的参数,只不过还

前端小组分享会之异步回调函数中的上下文

异步加载:又叫非阻塞加载,浏览器在下载执行js的同时,还会继续进行后续页面的处理.实现如:回调函数 .setTimeout . setInterval  回调函数(callback): 自己理解就是函数A里嵌套函数B B可能用到A中的变量,,B成为回调函数 function a (){ var x = 1; function b(){ console.log(++x) } b() } a() //2 上下文(Execution Context): 执行上下文(简称上下文)决定了Js执行过程中可以

异步回调函数-创建进程的三种方式

回调函数 有两个类,A,B,在类A中调用B,在B中调用A的方法完成A的工作,那么这个在B类中调用的A的函数就称为回调函数. 异步回掉函数:类A将自己的工作交给类B后,继续执行剩下的程序,而B继续完成A交给的工作. 使用方法: 1.定义一个接口 2.A可以直接继承此接口,也可以定义一个内部类继承此接口: 定义一个方法,调用B中的方法 3.B中的方法调用A中的方法. //定义接口 public interface doJob { public void fillBlank(int a,int b,i

C#异步回调函数

using System;using System.Collections.Generic;using System.Linq;using System.Text; namespace ComprehensiveTest.com{    public class AsyCallEx112    {        // 定义一个执行加法的委托        public delegate int sum(int a, int b);        public class number