第四章 管道和缓冲器
本文译自 《TangoRefMan_Sep_1_2008.odt 》
管道和缓冲器是tango IO 的基础。
管道(conduits)
Tango IO 是面向流的:每个数据最终被描述为一个Tango概念叫管道(conduits),包括InputStream和OutputStream。 例如,有一些管道用于文件、套接字(sockets)和控制台等的交互操作。一些管道要求明确的连接到终点,而另一些连接不明确(socket conduits是明确连接的一个变体)。文件和控制台管道通常是不明确地连接。有一些扩展让不同步读、写操作成为可能,通过各种具体操作系统方便的支持(见tanog.io.selector)。
管道是双向的,支持邻近的读和写,通过关联一个输入流(InputStream)进行读操作,通过关联一个输出流(OutputStream)进行写操作,访问分别通过管道的input()方法和output()方法。通常应用程序的交互作用是用管道流的read/write方法。每个方法接受一个无类型数组然后返回一个整数数组,描述构成量(quantitypopulated)和消耗量(consumed)。
注意在有些情况下,返回值少于数组长度(array-length)提供的值。实际上,保留常量IConduit.Eof返回流程末尾(end-of-flow)状态:
InputStream input();
OutputStream output();
InputStream
void clear();
Iconduit conduit();
uint read (void[] dst);
OutputStream
void flush();
Iconduit conduit();
uint write(void[] src);
OutputStream copy(InputStream src);
输出流方法(OutputStream)提供完整刷新一个数组并复制另一个管道的内容。这两种方法,可以同时完成,取决于管道末端(end-point)。
管道支持流过滤器的概念:一个或多个过滤器被绑到管道上,流过过滤器的数据会被拦截和改变。文件过滤器通过这些管道方法用颠倒顺序连接。返回上一个绑定的过滤器:
InputStream attach(InpuStream filteer);
OutputStream attach(OutpuStream filter);
管道过滤器像预期的一样链接在一起,如,一个过滤器read()方法调用它的祖先的read()方法。然而,如果看起来适合,过滤器会更改流向。过滤器基类在tango.io.Conduit模块中。
值得注意的是这些过滤器被有效地包容在管道自身之中,而不是把过滤器暴露出来作为IO活动的大门,这就允许管道细节(如socket属性、文件搜寻)隐藏在更少的界面之后。
再次提醒,管道不再需要使用时,应该被应用程序明确地关闭,以便于执行管道的清除行为并释放任何捆绑的过滤器。
管道异常
作为一个规则,管道不要乱扔异常,因为有些错误情况经常被认为是有效的。例如,IConduit.Eof一般完好无损地传回给应用程序。不过,如果底层操作系统检测到一些故障类型,异常还是会抛出。
缓冲器(Buffers)
缓冲一个管道应用到那里的内容相当于滑动窗口(sliding window),并且简化大多数通用操作如记号解析(token-parsing)和输出一系列相关事件,缓冲器也能提高可靠的IO应用程序的处理能力。例如 :缓冲使下面的管道读写大块的数据而不是分散的小块数据元素,它提供一个临时的空间给可靠的算法高效率地改变流内容;并且支持高效率地数据记录内容映射到用户内存。缓冲器是全双工的,它保持不同的读写存储位置——因此,当写到一个缓冲器的末尾附近时,可同时读另一个的开始部分。
缓冲器提供了必要的功能来处理许多类型的缓冲活动,从简单的附加操作到更平凡的生产/消费之间的管道带宽的平衡。同时也支持多个客户端。如,多于一个的reader和/或writer可能被捆绑到一个缓冲器,并且缓冲器将维护一个穿越它们(缓冲器)的常见的状态,这是非常方便的。例如 ,一个应用程序必须涉及一个混合或交叉存取(mixed or interleaved)协议(protocol)-----这样的客户端会连续操作,与并发形成对照。当使用一个没有协定的客户端时,缓冲器自身的数据类型是不可知的。这是一个聪明的数组,冲洗和/或通过管道回填本身是必要的。
一个缓冲器通常捆绑到一个管道,它能轻松的单独使用。如基于内存的累加器。在这些情况下,缓冲器客户端用完全相同的方式被添加,用相同的方式操作,有一个区别:一个没有绑定管道的缓冲器在填满容积时不能自动的被清仓,因此,这种情况发生时将抛出一个异常。不断增长的缓存可以用来处理这个情景。
一个缓冲器的通常用法包含下列方法:
Ibuffer append (void[] src);
uint read(void[] dst);
void[] slice();
Ibuffer fill();
Ibuffer flush();
Ibuffer clear();
Iconduit conduit();
这些方法通常是不常见:
bool truncate(uint extent);
bool truncate(uint extent);
bool next(uint delegate(void[] tokenizer);
Void[] getContent();
Ibuffer setContent(void[] content);
Ibuffer copy(Iconduit src);
Iconduit compress();
另一个缓冲器变体包装操作系统设施陈列内存映射文件(memory-mapped files)。缓冲内存(通常)被直接映射到一个大文件中,那个文件可以被看作刚才的那个缓冲器,或更确切地作为一个无类型数组(void [])。
缓冲器异常
当没有连接到一个管道,一个缓冲区溢出状态可能会发生,导致一个异常被抛出。在适当的地方,用一个扩大的缓冲器正确管理溢出条件。