一篇文章理解 Python 异步编程

Keil5安装多版本共存方案:实用技巧分享 分享Keil5安装过程中实现多版本共存的实用方法,解决开发环境冲突问题。通过合理配置路径与版本管理,让keil5安装更高效稳定,提升嵌入式开发体验。 阅读详情

点击上方“编程派”,选择设为“设为星标”

优质文章,第一时间送达!


作者:kingname@未闻Code

本文深入介绍了 asyncio 是如何通过单线程单进程实现并发效果的。以及异步代码是不是能在所有方面都代替同步代码。

一些例子

第一个例子

假设你需要用电饭煲煮饭,用洗衣机洗衣服,给朋友打电话让他过来吃饭。其中,电饭煲需要30分钟才能把饭煮好,洗衣机需要40分钟才能把衣服洗好,朋友需要50分钟才能到你家。那么,是不是你需要在这三件事情上面消耗30 + 40 + 50 = 120分钟?

实际上,在现实中你只需要消耗50分钟就可以了——

  1. 先给朋友打电话,让他现在出门

  2. 把衣服放进洗衣机并打开电源

  3. 把米淘洗干净,放进电饭煲并打开电源

然后,你要做的就是等待。

第二个例子

现在,你需要完成语文试卷,数学试卷和英语试卷。每张试卷需要做1小时。于是你需要1 + 1 + 1 = 3小时来完成所有的试卷。没人帮你,所以你没有办法在少于3小时的情况下完成这三张试卷。

第三个例子

现在,你需要用电饭煲煮饭、用洗衣机洗衣服,并完成一张数学试卷。其中,电饭煲需要30分钟才能把饭煮好,洗衣机需要40分钟才能把衣服洗好,试卷需要1小时才能完成。

但你并不需要30 + 40 + 60 = 130分钟。你只需要70分钟左右——

  1. 把衣服放进洗衣机并打开电源

  2. 把米淘洗干净,放进电饭煲并打开电源

  3. 开始完成试卷

能异步与不能异步

在第一个例子里面,煮饭、洗衣、等朋友有一个共同点,就是每个操作看似耗时很长,但真正需要人去操作的只有很少的时间:淘米、打开电饭煲电源用时5分钟;把衣服放进洗衣机,打开电源用时2分钟;给朋友打电话用时1分钟。剩下的大部分时间都不需要人来操作,都是等待即可。

再看第二个例子,每一张试卷都会占用整个你,没有等待的时间,所以必须一张一张试卷完成。

这两个例子实际上对应了两种程序类型:I/O 密集型程序和计算密集型程序。

我们在使用 requests 请求 URL、查询远程数据库或者读写本地文件的时候,就是 I/O操作。这些操作的共同特点就是要等待。

以 request 请求URL 为例,requests 发起请求,也许只需要0.01秒的时间。然后程序就卡住,等待网站返回。请求数据通过网络传到网站服务器,网站服务器发起数据库查询请求,网站服务器返回数据,数据经过网线传回你的电脑。requests 收到数据以后继续后面的操作。

大量的时间浪费在等待网站返回数据。如果我们可以充分利用这个等待时间,就能发起更多的请求。而这就是异步请求为什么有用的原因。

但对于需要大量计算任务的代码来说,CPU 始终处于高速运转的状态,没有等待,所以就不存在利用等待时间做其它事情的说法。

所以:异步只适用于 I/O 操作相关的代码,不适用于非 I/O操作。

Python 的异步代码

上面我们使用生活中的例子来说明异步请求,这可能会给大家一种误解——我可以控制代码,让代码在我想让他异步的地方异步,不想异步的地方同步。例如,可能有人会希望能用下面这段伪代码所描述方式来写代码:

请求 https://baidu.com,在网站返回期间:
    a = 1 + 1
    b = 2 + 2
    c = 3 + 3
拿到返回的数据,做其他事情

就像是我们把电饭煲的电源插上后,等待饭煮好的过程中,我可以看书,可以打电话,可以看电视,想做什么就做什么。

这段伪代码写得很符合直觉,但在使用 Python里面不能这样写。

下面我们用一段真正的代码,来说明这样写有什么问题。

首先,我们做一个网站,当我们请求http://127.0.0.1:8000/sleep/<num>时,网站会等待num秒才会返回。例如:http://127.0.0.1:8000/sleep/3表示,当你发起请求后,网站会等待3秒钟再返回。运行效果如下图所示。

现在,我们使用 aiohttp 发送3次请求,分别等待1秒、2秒、3秒返回:

import aiohttp
import asyncio
import time


async def request(sleep_time):
    async with aiohttp.ClientSession() as client:
        resp = await client.get(f'http://127.0.0.1:8000/sleep/{sleep_time}')
        resp_json = await resp.json()
        print(resp_json)


async def main():
    start = time.perf_counter()
    await request(1)
    a = 1 + 1
    b = 2 + 2
    print('能不能在第一个请求等待的过程中运行到这里?')
    await request(2)
    print('能不能在第二个请求等待的过程中运行到这里?')
    await request(3)

    end = time.perf_counter()
    print(f'总计耗时:{end - start}')


asyncio.run(main())

运行效果如下图所示:

在图中第15行代码,发起了1秒的请求,那么第15行应该会等待1秒钟才会返回数据。而第16、17、18行都是简单的赋值和 print 函数,运行时间加在一起都显然小于1秒钟,所以理论上我们看到的返回应该是:

能不能在第一个请求等待的过程中运行到这里?
能不能在第二个请求等待的过程中运行到这里?
{'success': True, 'time': 1}
{'success': True, 'time': 2}
{'success': True, 'time': 3}
总计耗时:3.018130547

但实际上,我们看到的效果,却是:程序先运行到第15行,等待请求完成网站返回以后,再运行16,17,18行,然后运行19行,等2秒请求完成了,再运行第20行,最后运行第21行。3次请求串行发出,最终耗时6秒。

程序的运行逻辑与我们期望的不一样。程序并没有利用 I/O 等待的时间发起新的请求,而是等上一个请求结束了再发送下一个请求。

问题出在哪里?

问题出现在,Python 的异步代码,请求之间的切换不能由开发者来直接管理。

开发者通过await语句告诉 asyncio,它后面这个函数,可以被异步等待。注意是可以被等待,但要不要等待,这是 Python 底层自己来决定的。

因为一个 I/O 操作,无论你是发网络请求,还是读写硬盘,Python 都知道,所以当 Python 发现你现在的这个操作确实是一个 I/O操作时,它才会利用I/O 等待时间。

所以,在 Python 的异步编程中,开发者能做的事情,就是把所有能够异步的操作,一批一批告诉 Python。然后由 Python 自己来协调、调度这批任务,并充分利用等待时间。开发者没有权力直接决定这些 I/O操作的调度方式。

所以,上面的代码我们需要做一些修改:

import aiohttp
import asyncio
import time


async def request(sleep_time):
    async with aiohttp.ClientSession() as client:
        resp = await client.get(f'http://127.0.0.1:8000/sleep/{sleep_time}')
        resp_json = await resp.json()
        print(resp_json)


async def main():
    start = time.perf_counter()
    tasks_list = [
        asyncio.create_task(request(1)),
        asyncio.create_task(request(2)),
        asyncio.create_task(request(3)),
    ]
    await asyncio.gather(*tasks_list)
    end = time.perf_counter()
    print(f'总计耗时:{end - start}')


asyncio.run(main())

运行效果如下图所示:

可以看到,现在耗时3秒钟,说明这3次请求,确实利用了请求的等待时间。

我们通过asyncio.create_task()把不同的协程定义成异步任务,并把这些异步任务放入一个列表中,凑够一批任务以后,一次性提交给asyncio.gather()。于是,Python 就会自动调度这一批异步任务,充分利用他们的请求等待时间发起新的请求。

我们平时在写 Scrapy 爬虫时,会有类似下面这样的代码:

...
yield scrapy.Request(url, callback=self.parse)
next_url = url + '&page=2'
yield scrapy.Request(next_url, callback=self.parse)

看起来像是先“请求”url,然后利用这个请求的等待时间执行next_url = url + '&page=2'接下来再发起另一个请求。

但实际上,在 Scrapy 内部,当我们执行yield scrapy.Request后, 仅仅是把一个请求对象放入 Scrapy 的请求队列里面,然后就继续执行next_url = url + '&page=2'了。

请求对象放进请求队列后,还没有真正发起 HTTP请求。只有凑够了一定数量的请求对象或者等待一段时间以后,Scrapy 的下载器才会统一调度这一批请求对象,统一发送 HTTP请求。当某个请求返回以后,Scrapy 把返回的 HTML 组装成 Response 对象,并把这个对象传入 callback 函数执行后续操作。

综上所述,在 Python 里面的异步编程,你需要先凑够一批异步任务,然后统一提交给 asyncio,让它来帮你调度这批任务。你不能像 JavaScrapt 中那样手动直接控制在异步请求等待时执行什么代码。

在异步代码中调用同步函数

在异步函数里面是可以调用同步函数的。但是如果被调用的同步函数很耗时,那么就会卡住其他异步函数。例如print函数就是一个同步函数,但是由于它耗时极短,所以不会卡住异步任务。

我们现在写一个基于递归的斐波那契数列第 n 项计算函数,并在另一个异步函数中调用它:

def sync_calc_fib(n):
    if n in [1, 2]:
        return 1
    return sync_calc_fib(n - 1) + sync_calc_fib(n - 2)


async def calc_fib(n):
    result = sync_calc_fib(n)
    print(f'第 {n} 项计算完成,结果是:{result}')
    return result

众所周知,基于递归的方式计算斐波那契数列第 n 项,速度非常慢,我们计算一下第36项,可以看到耗时在5秒钟左右:

如果我们把计算斐波那契数列(CPU 密集型)与请求网站(I/O密集型)任务放在一起会怎么样呢?

我们来看看效果:

可以看出,总共耗时8秒左右,其中计算斐波那契数列第36项耗时5秒,剩下3次网络请求耗时3秒,所以总共耗时8秒。

这段代码说明,当一个异步函数(calc_fib)中调用了一个耗时非常长的同步函数(sync_calc_fib)时,这一批所有的异步任务都会被卡住,只有这个同步函数运行完成以后,其他的异步函数才能被正常调度。这就是为什么在异步编程里面,不建议使用 time.sleep的原因。

回复下方「关键词」,获取优质资源

回复关键词「 pybook03」,立即获取主页君与小伙伴一起翻译的《Think Python 2e》电子版

回复关键词「入门资料」,立即获取主页君整理的 10 本 Python 入门书的电子版

回复关键词「m」,立即获取Python精选优质文章合集

回复关键词「book 数字」,将数字替换成 0 及以上数字,有惊喜好礼哦~

题图:pexels,CC0 授权。

好文章,我在看❤️

第29章:PCL PrincipalCurvatures 曲率估计 PrincipalCurvatures(主曲率估计)是一种用于计算点云表面曲率的算法。曲率描述了曲面局部的弯曲程度,是表面几何特征的重要指标。在点云处理中,估计表面的曲率对于形状分析、物体识别、表面重建等任务非常重要。主曲率是曲面上某一点的最大和最小曲率方向。通过计算点云的主曲率,我们可以获得点云中每个点的局部形状特征。通常情况下,主曲率包括两个主要参数:最大主曲率(最大曲率方向上的曲率)和最小主曲率(最小曲率方向上的曲率)。优点缺点提供了对点云表面局部形状的深入分析。 阅读详情

相关推荐

学术报告系列(二) - 智能自主作业机器人感知与控制技术及发展趋势

收集整理于2022年模式识别高峰论坛湖南大学王耀南院士报告(报名题目:智能自主作业机器人感知与控制技术及发展趋势)

Make something people want. 4699

python——异步编程原理

摘要 主要介绍python中相关的异步编程的原理 协程函数 在定义的时候使用async def 函数名(): 协程对象 执行协程函数()得到的协程对象 #协程函数 async def func(): pass #result 协程对象 内代码是不会执行的 result=func() 注意︰执行协程函数创建协程对象,函数内部代码不会执行。如果想要运行协程函数内部代码,必须要讲协程对象交给事件循环来处理。 import asyncio #协程函数 async def func():

庄小焱 1494

Linux环境下OpenSSH升级到 OpenSSH_9.8p1(内置保姆级教程并包含openssl升级过程)

2024年7月1日接到安全部门同事邮件通知,目前生产环境及测试环境服务器Openssh存在远程代码执行漏洞(CVE-2024-6387),漏洞等级高,且攻击者可以成功利用该漏洞获得远程root shell最高权限从而执行任意代码及命令,主要受影响版本为8.5p1

菜鸟运维升级之路 2万+

[进阶] --- Python3 异步编程详解(史上最全篇)

[进阶] - Python3 异步编程详解:https://blog.youkuaiyun.com/lu8000/article/details/45025987 参考:http://aosabook.org/en/500L/a-web-crawler-with-asyncio-coroutines.html 木风卜雨:https://blog.youkuaiyun.com...

墨鱼菜鸡 3583

电机驱动技术中关于磁阻转矩生成的理解

正交在物理学和数学中指的是两个方向或两个向量之间的夹角为90°(直角)。在图中,当气隙磁场的轴线与转子轴线的夹角为90°时,我们称它们为正交。转子磁极的方向。通常由转子的磁极中心指向另一极的连线表示。转子轴线用图示中的d 轴表示。通过对定子、转子、气隙磁场、磁力线的理解,我们可以清晰地分析不同角度下电磁转矩的方向。这种分析不仅有助于电机的理论学习,还能为实际设计提供重要参考。掌握这些基础知识后,可以更好地理解电机工作原理及优化其性能。

weixin_48327795的博客 2074

Ghidra逆向分析实战:从安装JDK到解析STM32的bin文件(附SVD插件配置)

本文详细介绍了使用Ghidra进行STM32固件逆向分析的完整流程,从JDK安装配置到SVD插件的应用。通过实战案例展示如何解析bin文件、恢复符号信息以及利用SVD插件识别外设寄存器,帮助开发者高效完成嵌入式设备的逆向工程任务。

weixin_29309475的博客 646

python3异步编程async/await 原理解释的比较详细的文章

身为Python核心开发组的成员,我对于这门语言的各种细节充满好奇。尽管我很清楚自己不可能对这门语言做到全知全能,但哪怕是为了能够解决各种issue和参与常规的语言设计工作,我也觉得有必要试着接触和理解Python的内核,弄清楚在底层它是怎么工作的。 话虽如此,直到最近我才理解了Python3.5中async/await的工作机制。在此之前,对于async/await语法,我只知道Python3.3中的yield from和Python3.4中的asyncio让这个新语法得以在Python3.5中实现。由

permike的专栏 1万+

中职网络安全2021年国赛Wireshark流量分析题目解析

中职网络安全Wireshark流量分析题目解析

FRANXX_02的博客 6444

『Python底层原理』--异步机制(async/await)

在现代编程中,并发是提高程序效率的关键技术之一,它允许程序同时执行多个任务,充分利用系统资源。 本文将深入探讨 Python 中的async/await机制,从并发编程基础讲起,逐步剖析其工作原理和实现方式。 1. 并发编程基础 计算机程序的执行方式主要有两种:顺序执行和并发执行。 顺序执行是按代码

fenfenfen520a的博客 1171

成功解决 OllamaError: status code 400, message: model ‘llama3.2:latest‘ not found 报错

本文针对Ollama更新后出现的OllamaError: status code 400, message: model 'llama3.2:latest' not found报错,分析了其根本原因——2026年1月Ollama将Llama 3.2模型命名从llama3.2:latest改为meta-llama/Llama-3.2:7b-instruct等新格式。解决方案包括:1)使用新模型名称;2)升级Ollama SDK;3)查询确认本地可用模型;4)批量更新旧项目脚本。通过正确适配新命名规则,

博客 1398

深度学习在传感器数据分析中的创新应用

传感器是物理世界与数字世界的“接口”,其生成的高维、时序、多源数据蕴含着设备状态、环境变化、用户行为等关键信息。传统传感器数据分析依赖手工特征工程与简单机器学习模型,难以应对复杂场景下的非线性、长程依赖与实时性需求。深度学习通过自动特征提取非线性建模与端到端学习,成为传感器数据处理的革命性工具。本文从概念基础理论框架架构设计实现机制到实际应用,系统拆解深度学习与传感器数据的融合逻辑,结合工业预测维护医疗健康监测智能交通等场景,揭示其技术边界与未来演化方向。

大数据洞察的博客 568

【碎片记】协程中使用 await 操作来等待 I/O 操作完成,原理是什么?

在协程中使用 `await` 操作会将协程从事件循环中移除,让出 CPU 资源,等待 I/O 操作完成后再重新加入到事件循环中。在协程中使用 `await` 操作来等待 I/O 操作的完成,避免了线程阻塞的问题,提高了 I/O 操作的效率和并发性能,让程序能够更好地利用 CPU 资源。在 Python 的协程中使用 `await` 来等待 I/O 操作的完成,其原理是协程在 I/O 操作执行期间将会被挂起,阻塞占用的 CPU 资源,直到 I/O 操作完成后再从挂起的地方恢复协程的执行。

MaxShuo的博客 835

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

python2异步编程_一篇文章理解 Python 异步编程

本文深入介绍了 asyncio 是如何通过单线程单进程实现并发效果的。以及异步代码是不是能在所有方面都代替同步代码。一些例子第一个例子假设你需要用电饭煲煮饭,用洗衣机洗衣服,给朋友打电话让他过来吃饭。其中,电饭煲需要30分钟才能把饭煮好,洗衣机需要40分钟才能把衣服洗好,朋友需要50分钟才能到你家。那么,是不是你需要在这三件事情上面消耗30 + 40 + 50 = 120分钟?实际上,在现实中你只...

weixin_39673471的博客 247

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

一篇文章理解Python异步编程的基本原理

摄影:产品经理与产品经理一起屯积一周的口粮未闻 Code 已经发布过很多篇关于异步爬虫与异步编程的文章,最近有读者希望我能深入介绍一下 asyncio 是如何通过单线程单进程实现并发效果...

cainiao_python的博客 870

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

python实现异步的原理_一篇文章理解Python异步编程的基本原理

一篇文章理解Python异步编程的基本原理摄影:产品经理与产品经理一起屯积一周的口粮未闻 Code 已经发布过很多篇关于异步爬虫与异步编程的文章,最近有读者希望我能深入介绍一下 asyncio 是如何通过单线程单进程实现并发效果的。以及异步代码是不是能在所有方面都代替同步代码。一些例子第一个例子假设你需要用电饭煲煮饭,用洗衣机洗衣服,给朋友打电话让他过来吃饭。其中,电饭煲需要30分钟才能把饭煮好,...

weixin_39540023的博客 407

python异步爬虫原理_一篇文章理解Python异步编程的基本原理

未闻 Code 已经发布过很多篇关于异步爬虫与异步编程的文章,最近有读者希望我能深入介绍一下 asyncio 是如何通过单线程单进程实现并发效果的。以及异步代码是不是能在所有方面都代替同步代码。一些例子第一个例子假设你需要用电饭煲煮饭,用洗衣机洗衣服,给朋友打电话让他过来吃饭。其中,电饭煲需要30分钟才能把饭煮好,洗衣机需要40分钟才能把衣服洗好,朋友需要50分钟才能到你家。那么,是不是你需要在这...

weixin_39884872的博客 104

python获取get请求的耗时时间_一篇文章理解Python异步编程的基本原理

本文要点:深入介绍一下 asyncio 是如何通过单线程单进程实现并发效果的,以及异步代码是不是能在所有方面都代替同步代码。一些例子第一个例子假设你需要用电饭煲煮饭,用洗衣机洗衣服,给朋友打电话让他过来吃饭。其中,电饭煲需要30分钟才能把饭煮好,洗衣机需要40分钟才能把衣服洗好,朋友需要50分钟才能到你家。那么,是不是你需要在这三件事情上面消耗30 + 40 + 50 = 120分钟?实...

weixin_33732812的博客 476

深入理解Python异步编程

深入理解Python异步编程 文章转载自: 驹说码事,内容有部分修改。 0 前言 很多朋友对异步编程都处于“听说很强大”的认知状态。鲜有在生产项目中使用它。而使用它的同学,则大多数都停留在知道如何使用 Tornado、Twisted、Gevent 这类异步框架上,出现各种古怪的问题难以解决。而且使用了异步框架的部分同学,由于用法不对,感觉它并没牛逼到哪里去,所以很多同学做 Web 后端服务时还是采用 Flask、Django等传统的非异步框架。 从上两届 PyCon 技术大会看来,异步编程已经成了 P

Just for fun 1256

生动理解 Python 的异步编程

  小编说   本文的译者是Python部落网站课程的学员“河马哥”(学员群里大家都这么叫,本人并不是叫这个名字)。河马哥的译文语言流畅、文辞通俗,甚至连行文情感的波折起伏都表现得淋漓尽致,做到这些对于一篇技术文章的翻译来说是十分不容易的。我认为这里大家应该给他一些掌声。也鼓励翻译社的其他译者向河马哥学习:你的译文好坏,关系着近7万Python学习者的学习兴趣,希望大家努力做出有品质的译文。   ---------下面是译文------------   异步编程的 Python 实现,以及应用场景。

linjingyg的博客 174

理解python异步编程与简单实现asyncio

在开始说明异步编程之前,首先先了解几个相关的概念。编程学习资料点击领取 阻塞 程序未得到所需计算资源时被挂起的状态。 程序在等待某个操作完成期间,自身无法继续干别的事情,则称该程序在该操作上是阻塞的。 常见的阻塞形式有:网络I/O阻塞、磁盘I/O阻塞、用户输入阻塞等。 阻塞是无处不在的,包括CPU切换上下文时,所有的进程都无法真正干事情,它们也会被阻塞。(如果是多核CPU则正在执行上下文切换操作的核不可被利用。) 简单的理解的话,阻塞就是A调用B,A会被挂起,一直等待B的结果,什么都...

m0_63171455的博客 1469

python异步编程实例_python 异步编程

Python 3.5 协程究竟是个啥Yushneng· Mar 10th, 2016作者是 Python 语言的核心开发人员,这篇文章也是我分享的,但是在翻译之前并没有看得太仔细。作者在这篇文章里先是是从 Python 异步编程的发展历史一直介绍到 Python 3.5 中async/await新特性的提出,又从底层的实现的差异一直延伸到完整的代码实例,来说明旧的生成器作为协程的“权宜之计”...

weixin_39585070的博客 400

深入理解python异步编程 中_这篇文章讲得精彩-深入理解 Python 异步编程(上)!

可惜,二和三现在还没有出来~~~~~~~~~~~~~~~~~~~~~~~~~~http://python.jobbole.com/88291/~~~~~~~~~~~~~~~~~~~~~~~~~~~在Python 3.3 引入yield from新语法之后,就不再推荐用yield去做协程。全都使用yield from由于其双向通道的功能,可以让我们在协程间随心所欲地传递数据。4.5.3 yield ...

weixin_39769187的博客 305

Python异步编程核心速成

Python异步编程通过非阻塞 I/O 和协程调度,在单线程内实现高并发,显著提升了 I/O 密集型应用的性能和资源利用率。尽管对代码结构和开发者有一定要求,但其在实时性、扩展性方面的优势使其成为现代高并发系统的核心技术之一。

北京锋通科技有限公司的技术blog 1690

Python异步编程:深入理解 async

异步模型是一种在单一线程内通过程序员显式控制的任务暂停与恢复,实现多个任务协作式穿插执行的并发编程范式,核心在于非阻塞操作与事件驱动。

软件架构师笔记 1286

Python 异步编程详解

本文主要介绍Python的异步编程。

经天纬地,格物致理 1225
上一篇: Python 会被 Go 取代吗?
下一篇: 实战:Flask + Vue 生成漂亮的词云
codingpy
博客等级 码龄11年 1873粉丝 104原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值