使用tcl中的线程,需要执行脚本并监视挂起的脚本的日志文件



我的场景是:使用tcl,我正在编写一个文件。然后我正在寻找该文件,并希望监视该文件在执行过程中生成的日志文件。如果日志文件的大小在2小时后没有改变,那么我需要停止文件的执行,并重新运行tcl脚本,该脚本将重新生成文件,然后源代码(生成源代码循环继续,直到文件执行完成)

这是我的场景的psedo代码:

set root /home/nikhil/
set group {all}
set TopScript [open $root/TopScript.tcl w] 
puts $TopScript "[exec perl $root/extract_excel.pl $group] n}"
Puts $TopScript "set logfilename $root/logfile"
puts $TopScript "source $root/main_1.tcl"
puts $TopScript "source $root/main_2.tcl"
close $TopScript
#Pseudo code for scenario what I want is:
thread::create {
exec tclsh /home/nikhil/TopScript.tcl
thread::wait
}
thread::create {
set flag_for_interupt 0
while{!flag_for_interupt} {
set old_log_size [file size $root/logfile]
after [expr {int(1000* 60* 60* 2)}]
set new_log_size [file size $root/logfile]
if{$old_log_size == $new_log_size} {
puts "I suspect Test is in hung state.... checking again after 2 hours.....n"
after [expr {int(1000* 60* 60* 2)}]
set $new_log_size [file size $root/logfile]
if{$old_log_size == $new_log_size} {
puts "nn Test is in hung state.... log has not updated since last 4 hours........nn"
}
##########  enter code to interupt main thread and execute again
set flag_for_inturept 1
}
} 
}

Tcl不在线程之间共享(正常)变量。相反,您需要通过在线程之间发送消息来工作。消息只是您要求另一个线程运行的一个(通常是短的)脚本(可以通过几种方式处理脚本的结果,包括同步等待或断开连接运行脚本)。大多数时候,您在接收线程中设置了一个过程来实际执行工作。

让我们重组您的等待线程,以这种方式操作:

set waiter [thread::create {
proc do {filename targetThread returnMessage} {
set delay [expr {int(1000* 60* 60* 2)}]
while true {
# This would be really a do-while loop, but we don't have those
while true {
set old_log_size [file size $filename]
after $delay
set new_log_size [file size $filename]
if {$old_log_size == $new_log_size} break
}
puts "I suspect Test is in hung state... checking again after 2 hours...n"
after $delay
set new_log_size [file size $filename]
if {$old_log_size == $new_log_size} break
}
puts "nn Test is in hung state... log has not updated since last 4 hours...nn"
# Send message to main thread to do something about the hung test
thread::send -async $targetThread $returnMessage
}
thread::wait
}]

我们会将线程设置为实际工作方式:

thread::send -async $waiter [list do $root/logfile [thread::current] {set test_hung 1}]

然而,其中唯一的长操作是对after的调用。(好吧,除非你在获取日志文件大小的操作系统调用方面非常不走运。)这意味着我们可以在线程中转换为使用异步形式,让线程在工作时可以被访问。

set waiter [thread::create {
proc do {filename targetThread returnMessage} {
set delay [expr {int(1000* 60* 60* 2)}]
set old_log_size [file size $filename]
# Schedule the run of do2 in two hours
after $delay [list do2 $filename $targetThread $returnMessage $delay $filename $old_log_size]
}
proc do2 {filename targetThread returnMessage delay filename old_log_size} {
set new_log_size [file size $filename]
if {$old_log_size == $new_log_size} {
puts "I suspect Test is in hung state... checking again after 2 hours...n"
# Schedule the run of do3 in another two hours
after $delay [list do3 $filename $targetThread $returnMessage $delay $filename $old_log_size]
} else {
# An update did happen; run ourselves again in two hours to compare to the new size
after $delay [list do2 $filename $targetThread $returnMessage $delay $filename $new_log_size]
}
}
proc do3 {filename targetThread returnMessage delay filename old_log_size} {
set new_log_size [file size $filename]
if {$old_log_size == $new_log_size} {
puts "nn Test is in hung state... log has not updated since last 4 hours...nn"
# Send message to main thread to do something about the hung test
thread::send -async $targetThread $returnMessage
} else {
# An update did happen; run ourselves again in two hours to compare to the new size
after $delay [list do2 $filename $targetThread $returnMessage $delay $filename $new_log_size]
}
}
thread::wait
}]

所以…我们获得了可管理性,但失去了可读性(使用的API是相同的)。不错,但不太好!(这种重组被称为向ContinuationPassingForm的转换,它往往会破坏代码的可读性。)在8.6中,我们可以做得更好,因为我们有可以向线程的事件循环屈服的协程。

set waiter [thread::create {
proc do {filename targetThread returnMessage} {
coroutine Coro[incr ::Coro] doBody $filename $targetThread $returnMessage
}
proc delayForTwoHours {} {
set delay [expr {int(1000* 60* 60* 2)}]
after $delay [info coroutine]
yield
}
proc doBody {filename targetThread returnMessage} {
while true {
while true {
set old_log_size [file size $filename]
delayForTwoHours
set new_log_size [file size $filename]
if {$old_log_size == $new_log_size} break
}
puts "I suspect Test is in hung state... checking again after 2 hours...n"
delayForTwoHours
set new_log_size [file size $filename]
if {$old_log_size == $new_log_size} break
}
puts "nn Test is in hung state... log has not updated since last 4 hours...nn"
# Send message to main thread to do something about the hung test
thread::send -async $targetThread $returnMessage
}
thread::wait
}]

这(仍然具有相同的API调用约定)提供了可管理性,但实际上所有代码(尤其是除了它们自己过程中的短位之外)看起来都与我编写的第一个版本相同。在底层,协程对延续传递形式进行重写,但这现在由Tcl运行时处理,而不需要在代码中显式完成。(此外,Tcl使用显式协程启动,但这反过来意味着它可以在多个堆栈级别上进行屈服,而无需其他一些语言的复杂屈服链。)


我把使用第二个或第三个版本作为根本不需要额外线程的代码版本的基础作为练习。在后台运行进程也不需要线程;整个管理过程可以只使用一个(用户可见的)线程。

最新更新