纠纷奇闻社交美文家庭
投稿投诉
家庭城市
爱好生活
创业男女
能力餐饮
美文职业
心理周易
母婴奇趣
两性技能
社交传统
新闻范文
工作个人
思考社会
作文职场
家居中考
兴趣安全
解密魅力
奇闻笑话
写作笔记
阅读企业
饮食时事
纠纷案例
初中历史
说说童话
乐趣治疗

一文轻松搞懂5种IO模型帮你彻底搞懂同步异步阻塞非阻塞

6月26日 遭人厌投稿
  1。1什么是IO
  我们都知道在UNIX世界里一切皆文件,而文件是什么呢?文件就是一串二进制流而已,其实不管是Socket,还是FIFO(FirstInputFirstOutput,先进先出队列))、管道、终端。对计算机来说,一切都是文件,一切都是流。在信息交换的过程中,计算机都是对这些流进行数据的收发操作,简称为IO操作(InputandOutput),包括往流中读出数据、系统调用Read、写入数据、系统调用Write。不过计算机里有那么多流,怎么知道要操作哪个流呢?实际上是由操作系统内核创建文件描述符(FileDescriptor,FD)来标识的,一个FD就是一个非负整数,所以对这个整数的操作就是对这个文件(流)的操作。我们创建一个Socket,通过系统调用会返回一个FD,那么剩下的对Socket的操作就会转化为对这个描述符的操作,这又是一种分层和抽象的思想。1。2IO交互流程
  通常用户进程中的一次完整IO交互流程分为两阶段,首先是经过内核空间,也就是由操作系统处理;紧接着就是到用户空间,也就是交由应用程序。具体交互流程如下图所示。
  内核空间中存放的是内核代码和数据,而进程的用户空间中存放的是用户程序的代码和数据。不管是内核空间还是用户空间,它们都处于虚拟空间中,Linux使用两级保护机制:0级供内核(Kernel)使用,3级供用户程序使用。每个进程都有各自的私有用户空间(03G),这个空间对系统中的其他进程是不可见的。最高的1G字节虚拟内核空间则为所有进程及内核共享。
  操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针传递数据。因为Linux使用的虚拟内存机制,必须通过系统调用请求Kernel来协助完成IO操作,内核会为每个IO设备维护一个缓冲区,用户空间的数据可能被换出,所以当内核空间使用用户空间的指针时,对应的数据可能不在内存中。
  对于一个输入操作来说,进程IO系统调用后,内核会先看缓冲区中有没有相应的缓存数据,如果没有再到设备中读取。因为设备IO一般速度较慢,需要等待,内核缓冲区有数据则直接复制到进程空间。所以,一个网络输入操作通常包括两个不同阶段。
  (1)等待网络数据到达网卡,然后将数据读取到内核缓冲区。
  (2)从内核缓冲区复制数据,然后拷贝到用户空间。
  IO有内存IO、网络IO和磁盘IO三种,通常我们说的IO指的是后两者。如下图所示是IO通信过程的调度示意。
  2。2五种IO通信模型
  在网络环境下,通俗地讲,将IO分为两步:第一步是等待;第二步是数据搬迁。
  如果想要提高IO效率,需要将等待时间降低。因此发展出来五种IO模型,分别是:阻塞IO模型、非阻塞IO模型、多路复用IO模型、信号驱动IO模型、异步IO模型。其中,前四种被称为同步IO,下面对每一种IO模型进行详细分析。2。1阻塞IO模型
  阻塞IO模型的通信过程示意如下图所示。
  当用户进程调用了recvfrom这个系统调用,内核就开始了IO的第一个阶段:准备数据。对于网络IO来说,很多时候数据在一开始还没有到达(比如,还没有收到一个完整的UDP包),这个时候内核就要等待足够的数据到来。而在用户进程这边,整个进程会被阻塞,当数据准备好时,它就会将数据从内核拷贝到用户内存,然后返回结果,用户进程才解除阻塞的状态,重新运行起来。几乎所有的开发者第一次接触到的网络编程都是从listen()、send()、recv()等接口开始的,这些接口都是阻塞型的。阻塞IO模型的特性总结如下表所示。
  2。2。2非阻塞IO模型
  非阻塞IO模型的通信过程示意如下图所示。
  当用户进程发出read操作时,如果内核中的数据还没有准备好,那么它并不会阻塞用户进程,而是立刻返回一个error。从用户进程角度讲,它发起一个read操作后,并不需要等待,而是马上就得到了一个结果,用户进程判断结果是一个error时,它就知道数据还没有准备好。于是它可以再次发送read操作,一旦内核中的数据准备好了,并且再次收到了用户进程的系统调用,那么它会马上将数据拷贝到用户内存,然后返回,非阻塞型接口相比于阻塞型接口的显著差异在于,在被调用之后立即返回。非阻塞IO模型的特性总结如下表所示。
  非阻塞模式套接字与阻塞模式套接字相比,不容易使用。使用非阻塞模式套接字,需要编写更多的代码,但是,非阻塞模式套接字在控制建立多个连接、数据的收发量不均、时间不定时,具有明显优势。2。2。3多路复用IO模型
  多路复用IO模型的通信过程示意如下图所示。
  多个进程的IO可以注册到一个复用器(Selector)上,当用户进程调用该Selector,Selector会监听注册进来的所有IO,如果Selector监听的所有IO在内核缓冲区都没有可读数据,select调用进程会被阻塞,而当任一IO在内核缓冲区中有可读数据时,select调用就会返回,而后select调用进程可以自己或通知另外的进程(注册进程)再次发起读取IO,读取内核中准备好的数据,多个进程注册IO后,只有一个select调用进程被阻塞。
  多路复用IO相对阻塞和非阻塞更难简单说明,所以额外解释一段,其实多路复用IO模型和阻塞IO模型并没有太大的不同,事实上,还更差一些,因为这里需要使用两个系统调用(select和recvfrom),而阻塞IO模型只有一次系统调用(recvfrom)。但是,用Selector的优势在于它可以同时处理多个连接,所以如果处理的连接数不是很多,使用selectepoll的WebServer不一定比使用多线程加阻塞IO的WebServer性能更好,可能延迟还更大,selectepoll的优势并不是对于单个连接能处理得更快,而是能处理更多的连接。多路复用IO模型的特性总结如下表所示。
  2。2。4信号驱动IO模型
  信号驱动IO模型的通信过程示意如下图所示。
  信号驱动IO是指进程预先告知内核,向内核注册一个信号处理函数,然后用户进程返回不阻塞,当内核数据就绪时会发送一个信号给进程,用户进程便在信号处理函数中调用IO读取数据。从上图可以看出,实际上IO内核拷贝到用户进程的过程还是阻塞的,信号驱动IO并没有实现真正的异步,因为通知到进程之后,依然由进程来完成IO操作。这和后面的异步IO模型很容易混淆,需要理解IO交互并结合五种IO模型进行比较阅读。信号驱动IO模型的特性总结如下表所示。
  2。2。5异步IO模型
  异步IO模型的通信过程示意如下图所示。
  用户进程发起aioread操作后,给内核传递与read相同的描述符、缓冲区指针、缓冲区大小三个参数及文件偏移,告诉内核当整个操作完成时,如何通知我们立刻就可以开始去做其他的事;而另一方面,从内核的角度,当它收到一个aioread之后,首先它会立刻返回,所以不会对用户进程产生任何阻塞,内核会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,内核会给用户进程发送一个信号,告诉它aioread操作完成。
  异步IO的工作机制是:告知内核启动某个操作,并让内核在整个操作完成后通知我们,这种模型与信号驱动IO模型的区别在于,信号驱动IO模型是由内核通知我们何时可以启动一个IO操作,这个IO操作由用户自定义的信号函数来实现,而异步IO模型由内核告知我们IO操作何时完成。
  异步IO模型的特性总结如下表所示。
  2。2。6易混淆的概念澄清
  在实际开发中,我们经常会听到同步、异步、阻塞、非阻塞这些概念,每次遇到的时候都会蒙圈,然后就查网上各种资料,结果越查越迷糊。大部分文章都千篇一律,没有说到本质上的区别,所以下次再碰到这些概念,印象还是比较模糊,尤其是在一些场景下觉得同步与阻塞、异步与非阻塞没什么区别,但其实这四个术语描述的还真不是一回事。
  下面我们来慢慢探讨它们之间的区别与联系,在这之前,我们还会经常看到下面的组合术语。
  (1)同步阻塞。
  (2)同步非阻塞。
  (3)异步阻塞。
  (4)异步非阻塞。
  在什么是同步和异步、阻塞和非阻塞的概念还没弄清楚之前,更别提上面这些组合术语了,只会让你更加困惑。
  1同步和异步
  同步和异步其实是指CPU时间片的利用,主要看请求发起方对消息结果的获取是主动发起的,还是被动通知的,如下图所示。如果是请求方主动发起的,一直在等待应答结果(同步阻塞),或者可以先去处理其他事情,但要不断轮询查看发起的请求是否有应答结果(同步非阻塞),因为不管如何都要发起方主动获取消息结果,所以形式上还是同步操作。如果是由服务方通知的,也就是请求方发出请求后,要么一直等待通知(异步阻塞),要么先去干自己的事(异步非阻塞)。当事情处理完成后,服务方会主动通知请求方,它的请求已经完成,这就是异步。异步通知的方式一般通过状态改变、消息通知或者回调函数来完成,大多数时候采用的都是回调函数。
  2阻塞和非阻塞
  阻塞和非阻塞在计算机的世界里,通常指针对IO的操作,如网络IO和磁盘IO等。那么什么是阻塞和非阻塞呢?简单地说,就是我们调用了一个函数后,在等待这个函数返回结果之前,当前的线程是处于挂起状态还是运行状态。如果是挂起状态,就意味着当前线程什么都不能干,就等着获取结果,这就是同步阻塞;如果仍然是运行状态,就意味着当前线程是可以继续处理其他任务的,但要时不时地看一下是否有结果了,这就是同步非阻塞。具体如下图所示。
  3实际生活场景
  同步、异步、阻塞和非阻塞可以组合成上面提到过的四种结果。
  举个例子,比如我们去照相馆拍照,拍完照片之后,商家说需要30min左右才能洗出来照片。
  (1)这个时候,如果我们一直在店里面什么都不干,一直等待直到洗完照片,这个过程就叫同步阻塞。
  (2)当然,大部分人很少这么干,更多的是大家拿起手机开始看电视,看一会儿就会问老板洗完没,老板说没洗完,然后接着看,再过一会儿接着问,直到照片洗完,这个过程就叫同步非阻塞。
  (3)由于店里生意太好了,越来越多的人过来拍,店里面快没地方坐了,老板说你把手机号留下,我一会儿洗好了就打电话告诉你过来取,然后你去外面找了一个长凳开始躺着睡觉等待老板打电话,什么都不干,这个过程就叫异步阻塞(实际不应用)。
  (4)当然实际情况是,大家可能会先去逛街或者吃饭,或者做其他活动,这样一来,两不耽误,这个过程就叫异步非阻塞(效率最高)。
  4小结
  从上面的描述中,我们能够看到阻塞和非阻塞通常是指在客户端发出请求后,在服务端处理这个请求的过程中,客户端本身是直接挂起等待结果,还是继续做其他的任务。而异步和同步则是对于请求结果的获取是客户端主动获取结果,还是由服务端来通知结果。从这一点来看,同步和阻塞其实描述的是两个不同角度的事情,阻塞和非阻塞指的是客户端等待消息处理时本身的状态,是挂起还是继续干别的。同步和异步指的是对于消息结果是客户端主动获取的,还是由服务端间接推送的。记住这两点关键的区别将有助于我们更好地区分和理解它们。2。2。7各IO模型的对比与总结
  其实前四种IO模型都是同步IO操作,它们的区别在于第一阶段,而第二阶段是一样的:在数据从内核拷贝到应用缓冲区期间(用户空间),进程阻塞于recvfrom调用。
  有人可能会说,NIO(NonBlockingIO)并没有被阻塞。这里有个非常狡猾的地方,定义中所指的IOOperation是指真实的IO操作。NIO在执行recvfrom的时候,如果内核(Kernel)的数据没有准备好,这时候不会阻塞进程。但是,当内核(Kernel)中数据准备好的时候,recvfrom会将数据从内核(Kernel)拷贝到用户内存中,这个时候进程就被阻塞了。在这段时间内,进程是被阻塞的。下图是各IO模型的阻塞状态对比。
  从上图可以看出,阻塞程度:阻塞IO非阻塞IO多路复用IO信号驱动IO异步IO,效率是由低到高的。最后,再看一下下表,从多维度总结了各IO模型之间的差异,可以加深理解。
  2。3从BIO到NIO的演进
  下表总结了JavaBIO(BlockingIO)和NIO(NonBlockingIO)之间的主要差异。
  2。3。1面向流与面向缓冲
  JavaNIO和BIO之间第一个最大的区别是,BIO是面向流的,NIO是面向缓冲区的。JavaBIO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方。此外,不能前后移动流中的数据。如果需要前后移动从流中读取的数据,需要先将它缓存到一个缓冲区。JavaNIO的缓冲导向方法略有不同。数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动。这就增加了处理过程的灵活性。但是,还需要检查该缓冲区是否包含所有需要处理的数据。而且,要确保当更多的数据读入缓冲区时,不能覆盖缓冲区里尚未处理的数据。2。3。2阻塞与非阻塞
  JavaBIO的各种流是阻塞的。这意味着,当一个线程调用read()或write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情。JavaNIO的非阻塞模式,是一个线程从某通道(Channel)发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用,就什么都不会获取,而不是保持线程阻塞,所以直到数据变成可以读取之前,该线程可以继续做其他的事情。非阻塞写也是如此。一个线程请求写入某通道一些数据,但不需要等待它完全写入,这个线程同时可以去做别的事情。线程通常将非阻塞IO的空闲时间用于在其他通道上执行IO操作,所以一个单独的线程现在可以管理多个IO通道。2。3。3选择器在IO中的应用
  JavaNIO的选择器(Selector)允许一个单独的线程监视多个输入通道,可以注册多个通道使用一个选择器,然后使用一个单独的线程来选择通道:这些通道里已经有可以处理的输入,或者选择已准备写入的通道。这种选择机制使一个单独的线程很容易管理多个通道。2。3。4NIO和BIO如何影响应用程序的设计
  无论选择BIO还是NIO工具箱,都可能会影响应用程序设计的以下几个方面。
  (1)对NIO或BIO类的API调用。
  (2)数据处理逻辑。
  (3)用来处理数据的线程数。
  1API调用
  当然,使用NIO的API调用看起来与使用BIO时有所不同,但这并不意外,因为并不是仅从一个InputStream逐字节读取,而是数据必须先读入缓冲区再处理。
  2数据处理
  使用纯粹的NIO设计相较BIO设计,数据处理也会受到影响。
  在BIO设计中,我们从InputStream或Reader逐字节读取数据。假设你正在处理一个基于行的文本数据流,有如下一段文本。
  该文本行的流可以这样处理。
  请注意处理状态由程序执行多久决定。换句话说,一旦reader。readLine()方法返回,你就知道文本行肯定已读完,readline()阻塞直到整行读完,这就是原因。你也知道此行包含名称;同样,第二个readline()调用返回的时候,你知道这行包含年龄。正如你可以看到,该处理程序仅在有新数据读入时运行,并知道每步的数据是什么。一旦正在运行的线程已处理过读入的某些数据,该线程不会再回退数据(大多如此)。下图也说明了这条原则。
  JavaBIO从一个阻塞的流中读数据,而一个NIO的实现会有所不同,下面是一个简单的例子。
  注意第二行,从通道读取字节到ByteBuffer。当这个方法调用返回时,你不知道你所需的所有数据是否在缓冲区内。你所知道的是,该缓冲区包含一些字节,这使得处理有点困难。
  假设第一次read(buffer)调用后,读入缓冲区的数据只有半行,例如,Name:An,你能处理数据吗?显然不能,需要等待,直到整行数据读入缓存。在此之前,对数据的任何处理都毫无意义。
  所以,你怎么知道是否该缓冲区包含足够的数据可以处理呢?好了,你不知道。发现的方法只能查看缓冲区中的数据。其结果是,在你知道所有数据都在缓冲区里之前,你必须检查几次缓冲区的数据。这不仅效率低下,而且会使程序设计方案杂乱不堪。例如:
  bufferFull()方法必须跟踪有多少数据读入缓冲区,并返回真或假,这取决于缓冲区是否已满。换句话说,如果缓冲区准备好被处理,那么表示缓冲区已满。
  bufferFull()方法扫描缓冲区,但必须保持与bufferFull()方法被调用之前状态相同。如果没有,下一个读入缓冲区的数据可能无法读到正确的位置。虽然这是不可能的,但却是需要注意的又一个问题。
  如果缓冲区已满,它可以被处理。如果它不满,并且在实际案例中有意义,或许能处理其中的部分数据,但是许多情况下并非如此。下图展示了缓冲区数据循环就绪。
  3设置处理线程数
  NIO可以只使用一个(或几个)单线程管理多个通道(网络连接或文件),但付出的代价是解析数据可能会比从一个阻塞流中读取数据更复杂。
  如果需要管理同时打开的成千上万个连接,这些连接每次只是发送少量的数据,例如聊天服务器,实现NIO的服务器可能是一个优势。同样,如果需要维持许多打开的连接,如P2P网络中,使用一个单独的线程来管理所有出站连接,可能是一个优势。一个线程有多个连接的设计方案如下图所示。
  (1)JavaNIO:单线程管理多个连接。如果有少量的连接使用非常高的带宽,一次发送大量的数据,也许用典型的IO服务器实现可能非常契合。下图说明了一个典型的IO服务器设计。
  (2)JavaBIO:一个典型的IO服务器设计。一个连接只用一个线程来处理。2。4JavaAIO详解
  JDK1。7(NIO2)才是实现真正的异步AIO(AsynchronousIO)、把IO读写操作完全交给操作系统,学习了LinuxEpoll模式,下面我们来做一些演示。2。4。1AIO基本原理
  JavaAIO处理API中,重要的三个类分别是:AsynchronousServerSocketChannel(服务端)、AsynchronousSocketChannel(客户端)及CompletionHandler(用户处理器)。CompletionHandler接口实现应用程序向操作系统发起IO请求,当完成后处理具体逻辑,否则做自己该做的事情,真正的异步IO需要操作系统更强的支持。
  在多路复用IO模型中,事件循环将文件句柄的状态事件通知给用户线程,由用户线程自行读取数据、处理数据。而在异步IO模型中,当用户线程收到通知时,数据已经被内核读取完毕,并放在了用户线程指定的缓冲区内,内核在IO完成后通知用户线程直接使用即可。异步IO模型使用Proactor设计模式实现这一机制,如下图所示。
  2。4。2AIO初体验
  我们基于AIO先写一段简单的代码,来感受一下服务端和客户端的交互过程,同时也体验一下API的使用。先来看服务端代码。
  上述代码的主要功能就是开启一个监听端口,然后在CompletionHandler中处理接收到消息以后的逻辑,将接收到的信息再输出到客户端。下面来看客户端的代码。
  客户端的代码的主要功能是发送一串字符到服务端。同时,在CompletionHandler接口处理服务端发送过来的结果。
  服务端执行结果如下图所示。
  客户端执行结果如下图所示。
  运行代码后,我们会发现不管是客户端还是服务端,其处理接收消息的逻辑都是异步操作,和BIO、NIO的API使用有根本上的区别。
投诉 评论 转载

京东物流成立四大独立事业部,要把效率提升到极致京东物流组织架构调整刘强东的改革大刀,终于挥向了京东物流。4月12日,京东物流开启新一轮组织架构调整,具体分为两个方面。在总部层面,京东物流推行事业部制,成立……一文轻松搞懂5种IO模型帮你彻底搞懂同步异步阻塞非阻塞1。1什么是IO我们都知道在UNIX世界里一切皆文件,而文件是什么呢?文件就是一串二进制流而已,其实不管是Socket,还是FIFO(FirstInputFirstOut……国乒小龙女走红,有望接班陈梦,合影樊振东被网友调侃般配近日,随着国乒温州集结备战世乒赛,刚刚经历了陕西全运会以及乒超联赛征战的队员们,再次成为了外界关注的焦点。由于队中包括方博、周雨、顾玉婷等13位球员,宣布结束自己的国家队……我觉得有必要囤避孕套了啊,下一个办法会是什么四川取消生育结婚限制,这就是只要女性愿意,就可以生孩子了,可很多单身女性由于工作压力大,或是薪资待遇限制,还有带娃的压力,要是没人帮着带孩子。那还是会不愿意生孩子。大家都……天冷了,这才是你应该知道的各位看官小姐姐,每日分享来袭,端好小板凳准备围观哦。我们这里的天气,说冷就一下冷了。前两天还可以穿短袖,这两天就把羽绒服翻出来穿上了。这善变的天气就和女人的脸一样。。嘻嘻……眼下小颗粒脂肪粒2个方法变平滑很多人眼睛周围都有些小颗粒,有的是脂肪粒,用护肤品可以去除的。而有些小颗粒并不是脂肪粒这么简单,也有可能是汗管瘤,护肤品对它毫无作用。首先你要知道眼下的小颗粒到底属于哪一种,因……超前的诺基亚6630复刻版概念图,机身下巴是圆形如果你是80后,肯定对诺基亚6系列印象深刻,该系列不仅出过6600、6670、6680等一系列经典机型,甚至还有一款型号为6630的水货机风靡全国。如今,网友重制了诺基亚663……李铁再立功!又一大老虎带走,128天落马8人!4名裁判被带走近期,足球反腐持续深入。2022年11月26日国足原主教练李铁被查,至2023年3月29日,123天内中国足协前任、现任高层,已有8人落马。最新消息显示,中国田径协会主席……用5天4晚深度游玩广东潮州,总结了这几个地方不容错过!收藏真是计划不如变化,广东潮汕旅行,本想在潮州住两晚再去汕头住两晚。谁知道因为太喜欢,4个晚上都给了潮州。大概没有几个游客像我这样,能在潮州住上4个晚上的吧?网上的攻略上,大……元旦小长假游红河,就走这两条线路元旦游红河推荐两条精品线路元旦小长假的到来,想必能给大家紧张的岁末工作带来几许轻松惬意。冬日暖阳下的红河游,这两条精品线路值得来好好逛一逛。线路一:……1勺猪油10副药,轻轻松松搞定这些病一口就能让人唇齿留香的油渣和猪油拌饭,是很多人儿时不可磨灭的记忆。但随着三高等心血管疾病的增加,让很多人不得不放弃它。其实说猪油对身体健康有危害是有前提的,因为生活越来越……电信诈骗随时出现!请保持清醒头脑。一个小时前、一个显示中国大连陌生电话:19211310073的号码打给我。是个普通话不太标准的女声。问是不是我。确定后、说我注册京东APP时、勾选了京东金条、现在京东要整改金条……
精灵宝可梦极巨专属招式解析极光旋律热情拥抱资源再生旗舰级性能红米Note11TPro登场,红米Note11价崩体育精神的绝佳体现中美冰壶队让壶记为什么日本主妇丝毫不显老?打扮减龄妆发精致,活该她们美非洲故事6来到了肯尼亚北京买10套房子还要割韭菜,清冷的王菲与张柏芝相比,不够接地吃饱以后,躺着坐着还是站着?老人睡午觉是好习惯吗?了解下下单性能实力派蓝戟A750Photon还送价值279元机械键Ipodtouch4一部手机,一段回忆小米MIXFold2再升级,对标OPPOFindN,最具性价春天最易耗肝血,一个中成药一补到位一周热点丨可控的核聚变,不可控的未来
论企业发展自主型安全管理文化的重要性借款合同印花税纳税人是谁生了宝宝更要强大个技巧帮你变职场虎妈奶水太多每天晚上都涨得疼怎么回事?假如世界没有绿记叙文火祭为什么变绿了菜叶上的飞虱可以吃吗车子过户可以委托么热评聚热点网 我是千年转世的白狐只为你青春期最有效祛痘治疗方法教你怎么消除青春痘爆笑神回复你是明白了哪几个基本原理之后而厨艺大增的?明年的5GiPhone12手机你会去换吗?苹果市值或再创辉煌

友情链接:中准网聚热点快百科快传网快生活快软网快好知文好找美丽时装彩妆资讯历史明星乐活安卓数码常识驾车健康苹果问答网络发型电视车载室内电影游戏科学音乐整形