コマンドの状態が相互に依存していない場合は、コマンドを並列化できます。これを行うには多くの方法がありますが、より簡単な方法の1つは、スレッドパッケージのスレッドプーリングを使用することです(これには、現在多くのプラットフォームで標準となっているスレッドTclが必要です)。
package require Thread
set pool [tpool::create -maxworkers 4]
# The list of *scripts* to evaluate
set tasks {
{command 1}
{command 2}
...
{command n}
}
# Post the work items (scripts to run)
foreach task $tasks {
lappend jobs [tpool::post $pool $task]
}
# Wait for all the jobs to finish
for {set running $jobs} {[llength $running]} {} {
tpool::wait $pool $running running
}
# Get the results; you might want a different way to print the results...
foreach task $tasks job $jobs {
set jobResult [tpool::get $pool $job]
puts "TASK: $task"
puts "RESULT: $jobResult"
}
主な調整可能な値は、スレッドプールのサイズです。デフォルトは4の制限です(上記で明示的にリストした-maxworkers
オプションを使用して設定しtpool::create
ます)。選択する最適な値は、使用しているCPUコアの数によって異なります。各タスクが平均して生成するCPU負荷。測定して調整する必要があります…</p>
このオプションを使用し-initcmd
て、プール内の各ワーカースレッドに任意のスクリプトをプリロードすることもできます。それはあなたのpackage require
電話をかけるのに良い場所です。ワーカーはすべて、互いに完全に独立しており、マスタースレッドからも完全に独立しています。それらは状態を共有しません。各コードを別々のプロセスで実行すると、同じモデルが得られます(ただし、調整を行うためにさらにコードを作成することになります)。
[編集]:これはTcl 8.4で動作し、代わりにサブプロセスを使用するバージョンです。
namespace eval background {}
proc background::task {script callback} {
set f [open |[list [info nameofexecutable]] "r+"]
fconfigure $f -buffering line
puts $f [list set script $script]
puts $f {fconfigure stdout -buffering line}
puts $f {puts [list [catch $script msg] $msg]; exit}
fileevent $f readable [list background::handle $f $script $callback]
}
proc background::handle {f script callback} {
foreach {code msg} [read $f] break
catch {close $f}
uplevel "#0" $callback [list $script $code $msg]
}
proc accumulate {script code msg} {
puts "#### COMMANDS\n$script"
puts "#### CODE\n$code"
puts "#### RESULT\n$msg"
# Some simple code to collect the results
if {[llength [lappend ::accumulator $msg]] == 3} {
set ::done yes
}
}
foreach task {
{after 1000;subst hi1}
{after 2000;subst hi2}
{after 3000;subst hi3}
} {
background::task $task accumulate
}
puts "WAITING FOR TASKS..."
vwait done
注:タスクは結果を生成するTclコマンドですが、結果を出力してはなりません。ファブリックコード(in background::task
)がそれを処理します。これらはサブプロセスです。それらは互いに何も共有しないため、タスクの一部として送信する必要があります。より洗練されたバージョンでは、サブプロセスのホットプールを維持でき、一般にスレッドプールのように機能します(スレッドではなくサブプロセスにあるため、微妙な違いがあります)が、これは私がここに書きたかったよりも多くのコードでした。
結果コード(つまり、例外コード)は、「ok」の場合は0、「error」の場合は1、およびあまり一般的でない場合はその他の値です。これらは、 Tcl8.6のcatch
マニュアルページに記載されている値とまったく同じです。それらを正しく解釈するのはあなた次第です。::errorInfo
(エラーが発生した場合に変数の内容を報告するコードも追加する必要があると思います::errorCode
が、コードがかなり複雑になります…)