数据I/O的作用域内对象:利用垃圾回收机制清理LUN和文件ID
6856 为本文评分:
5.0
数据I/O的作用域内对象:利用垃圾回收机制清理LUN和文件ID
匿名 2015年9月17日,星期四
在IDL中编写程序时,我发现要记住必须始终清理程序中创建的任何文件LUN或文件ID,这非常繁琐。无论程序从哪个出口退出,我都必须这样做。否则,最终我可能会遇到这个令人沮丧的错误信息:
% OPENR:所有可用逻辑单元当前均在使用中。
然后,我必须调试我的程序,查看它在何处打开了文件但没有关闭它们。
除了在任何RETURN语句之前立即关闭文件外,我还必须记住,我编写的任何例程或子例程都需要考虑在发生错误时关闭在该例程中打开的文件。因此,我的例程需要包含一个类似这样的捕获块:
CATCH, err
IF err NE 0 THEN BEGIN
CATCH, /CANCEL
IF N_ELEMENTS(unit) GT 0 THEN BEGIN
FREE_LUN, unit
ENDIF
MESSAGE, /REISSUE_LAST
ENDIF
这不仅是我经常忘记的事情,而且使用调用REISSUE_LAST的捕获块会使例程的调试变得困难。遇到错误时,IDL将执行捕获块,然后在REISSUE_LAST行停止,而不是在错误实际发生的那一行停止。
创建作用域内对象
上述捕获块的替代方法是创建一个简单的对象,该对象在初始化时打开文件,然后在超出作用域时关闭文件。如果对象的实例是在例程或子例程内部创建的,那么只要它没有被传递到其所在的例程之外,IDL的自动垃圾回收机制就会在例程退出时销毁该对象。当对象被销毁时,将调用该对象的::Cleanup方法,在那里可以进行文件关闭操作。这样做的好处是,文件关闭逻辑只需要在一个地方编写,并且它总是会被执行。
下面是一个作用域内文件管理对象的示例,它在Init时接受一个文件名,并且在对象被销毁时自动释放文件单元。
**FUNCTION InScopeFileManager::Init, filename
COMPILE_OPT IDL2
OPENR, unit, filename, /GET_LUN
; 将单元存储为成员变量。
self.unit = unit
IF (~self.IDL_Object::Init()) THEN RETURN, 0
RETURN, 1
END
;------------------------------------------------
PRO InScopeFileManager::GetProperty, UNIT=unit
COMPILE_OPT IDL2
IF (ARG_PRESENT(unit)) THEN unit = self.unit
END
;------------------------------------------------
PRO InScopeFileManager::Cleanup
COMPILE_OPT IDL2
IF (self.unit GT 0) THEN FREE_LUN, self.unit
END
;------------------------------------------------
PRO InScopeFileManager__Define
COMPILE_OPT IDL2
!NULL = {InScopeFileManager, $
INHERITS IDL_Object, $
unit: 0}
END**
利用作用域内文件管理对象
现在我已经实现了一个作用域内对象来管理我的文件,我可以在需要时调用它,而不是直接调用文件打开。
下面的示例例程将使用该对象来打开一个文件。然后,它将逐行遍历文件的每一行,分析每一行以确定该行是否包含我想要的某些信息。如果找到了我想要的信息,那么例程就完成了,它会在不需要读完整个文件的情况下返回。
请注意,当例程完成时,对象将超出作用域,IDL将自动为我清理它。在这个例程中,我不需要做任何事情来确保文件在完成后被关闭,包括当我调用早期返回时。
PRO ReadMyFile, filename, output
COMPILE_OPT IDL2
fileMng = InScopeFileManager(filename)
nLines = FILE_LINES(filename)
FOR i=0, nLines-1 DO BEGIN
line = ''
READF, fileMng.UNIT, line
; 调用一个函数来分析文件中的一行
; 并确定该行是否包含我
; 正在寻找的内容。
AnalyzeLine, line, output, HAS_WHAT_I_WANT=hasWhatIwant
; 如果这一行包含我想要的内容,那么返回并且不
; 读取文件的其余部分。
IF (hasWhatIWant) THEN RETURN
ENDFOR
END
如果我想在例程内手动关闭文件,例如,如果我正在循环处理多个文件,并希望在打开下一个文件之前关闭前一个文件,我只需要调用
OBJ_DESTROY, fileMng
并且Cleanup方法将执行释放LUN的工作。
在上面的例子中,我可以通过调用以下命令来验证运行例程后文件是否正确关闭
HELP, /FILES
该文件不应该在IDL中显示为打开状态。
作用域内对象的其他用途
除了管理文件单元外,这一原则还可用于其他类型的文件句柄,例如在处理HDF数据时跟踪文件和数据集标识符。
此外,作用域内对象可以在读取数据之外的场景中使用。例如,如果一个例程显示了一个小部件对话框,例如状态消息或进度条,并且您希望确保在例程退出时小部件始终被关闭,那么您可以将小部件包装到一个对象中,就像上面的对象一样,其中Cleanup方法将包含如下一行:
WIDGET_CONTROL, self, widgetId, /DESTROY
当例程退出时,对象将超出作用域,它将被垃圾回收,并且小部件将被销毁。
总之,我必须说,尽管前期需要额外的工作来创建一个作用域内对象,而不仅仅是使用捕获块或手动进行文件关闭调用,但我发现从长远来看,使用这种方法为我节省了时间并减少了挫折感。