5

複数のシェル コマンドを連続して実行する tcl スクリプトがあります。

このようなもの:

abc.tcl

command 1
command 2 
command 3
...
command n

このスクリプトは、これらのコマンドの出力を次の形式でテキスト ファイルに出力します。

### ### ### ### ### ###
Command name
### ### ### ### ### ###

Command Output

### ### ### ### ### ##

スクリプトをより高速に実行しようとしていましたが、シェルコマンドをシリアルではなくパラレルで実行しました。それらをバックグラウンドにプッシュする (& コマンド)。しかし、以前のように出力テキスト ファイルの書式を保持する方法がわかりません。

コマンドをバックグラウンドにプッシュすると、それらの出力を一時ファイルに追加する必要がありますが、これらのファイルには、コマンドの出力が一緒にダンプされているだけです。異なる出力を区別することは困難です。

バックグラウンドで実行されている各コマンドの出力を個々の一時ファイルにリダイレクトできる方法はありますか (一時ファイルの名前には、バックグラウンドで実行されているプロセスのプロセス ID を含めることができます)。すべてのコマンドが実行されたら、出力をまとめて適切な形式にすることができますか? これを達成する方法についてのアイデア/提案。

4

1 に答える 1

3

コマンドの状態が相互に依存していない場合は、コマンドを並列化できます。これを行うには多くの方法がありますが、より簡単な方法の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が、コードがかなり複雑になります…)

于 2012-10-26T08:18:27.770 に答える