我有一个包装在Tcl中的C函数,该函数可打开文件,读取内容,执行操作并返回值。不幸的是,当我调用该函数打开大文件时,它阻止了事件循环。操作系统是linux。
我想使通话异步。我该怎么做?
(我可以将工作传递给另一个Tcl线程,但这并不是我想要的)。
通常这很难做到。问题在于,由于操作系统级别涉及抽象,异步文件操作不适用于普通文件。最好的解决方法(如果可以)是首先在文件上建立索引,这样就可以避免通读全部内容,而只读seek
到靠近数据的地方。这是数据库工作方式的核心。
如果你不能做到这一点,但你可以应用一个简单的过滤器,把该过滤器在子进程(管道做工作,在Tcl的异步I / O,而他们这样做的所有支持的平台上),或者另一个线程(线程间的消息从异步处理的角度来看也很不错)可以创造奇迹。
如果可以,请使用上述技术。他们是我认为您应该做的。
如果那是不切实际的,那么您将不得不采用困难的方式进行此操作。困难的方法涉及在处理中插入事件循环感知延迟。
在Tcl 8.5及更高版本中,您可以通过以下步骤将代码分成不同的过程,并使用这样的节通过“延迟”在它们之间传递控制权:
# 100ms delay, but tune it yourself
after 100 [list theNextProcedure $oneArgument $another]
这是延续传递的风格,正确行事可能很棘手。特别是,它对复杂的处理相当混乱。例如,假设您正在文件的前几千行循环:
proc applyToLines {filename limit callback} {
set f [open $filename]
for {set i 1} {$i <= $limit} {incr i} {
set line [gets $f]
if {[eof $f]} break
$callback $i $line
}
close $f
}
applyToLines "/the/filename.txt" 1000 DoSomething
在经典的Tcl CPS中,您可以这样做:
proc applyToLines {filename limit callback} {
set f [open $filename]
Do1Line $f 1 $limit $callback
}
proc Do1Line {f i limit callback} {
set line [gets $f]
if {![eof $f]} {
$callback $i $line
if {[incr i] <= $limit} {
after 10 [list Do1Line $f $i $limit $callback]
return
}
}
close $f
}
applyToLines "/the/filename.txt" 1000 DoSomething
如您所见,这不是一个简单的转换,并且如果您希望在处理完成后执行某项操作,则需要传递一个回调。(您也可以使用全局变量,但这并不优雅……)
(如果您需要帮助来更改代码以使其正常工作,则需要向我们展示您需要帮助的代码。)
在Tcl 8.6中,尽管上述代码技术仍然可以使用,但是您还有另一个选择:协程!我们可以这样写:
proc applyToLines {filename limit callback} {
set f [open $filename]
for {set i 1} {$i <= $limit} {incr i} {
set line [gets $f]
if {[eof $f]} break
yield [after 10 [info coroutine]]
$callback $i $line
}
close $f
}
coroutine ApplyToAFile applyToLines "/the/filename.txt" 1000 DoSomething
这是几乎相同的,除了用线yield
及info coroutine
(其暂停协程,直到它从在10ms左右的时间事件循环恢复),并用线coroutine ApplyToAFile
,其中该前缀创建一个协程(给定任意名称ApplyToAFile
),并将其设置跑步。如您所见,像这样转换您的代码并不难。
(协程引擎完全没有机会移植到8.5或更早版本;它完全需要8.6中的非递归脚本执行引擎。)
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句