Tclにはイベントドリブンで処理を行う方法が用意されています。
proc record1 { } {
global s
gets $s data
set f [open "c:/temp/record.txt" a+]
puts $f $data
close $f
}
set s [socket 127.0.0.1 8097]
fconfigure $s -blocking 0 -buffering line -translation auto
fileevent $s readable record1
vwait a
こういうコードを書くと、サーバ(この場合localhost)のポート8097に接続しサーバから送られてくるデータをファイルに保存していきます。fileeventがTclのイベント処理に登録するコマンドで、読み取り可能なデータが入ってくると関数record1が呼び出されるようにしています。vwaitが変数aに値がセットされるまでイベント処理を続けるコマンドですが、このコードには変数aをセットするところがありませんので延々とイベント処理が続きます。
一方Tcl環境をC#等Managedな環境と一緒に使う場合Tcl/CSharp やEagle といった間を取り持つ層を用いますが、Tcl/CSharpを用いて以下のことをやるとします。
- C#側でソケット接続を受け付けるようにする
- C#側で同じプロセスの別スレッドからTcl interpreterを作成する
- 作成したTcl interpreterから先ほどのC#で作ったソケットサーバに接続する
- Tcl側で上のようなfileeventを使う方法で、C#側とソケット通信する
なぜこんな変なことをするのかは聞かないで(^^;) 実際には通信を開始する前にTcl interpreterにはコマンド追加などさまざまな操作をしており、それが今回の問題を引き起こしているかもしれないのですが、その内容はここには書けませんw
ここで問題が発生しまして、
- Tcl側でデータを一切受信しない。上の例で言えばrecord1が呼ばれません。
- ソケット接続は行われるのでTcl側が全く動いていないわけではない。
- telnetや即席のソケット通信プログラムを使用する限りC#側のソケットサーバーはデータを送信しているので、C#側に問題はない模様。
- ソケット通信TclスクリプトをC#とは別プロセス(別途起動したtclshやwish)から立ち上げるとちゃんと通信する。
これを解決するためTclスクリプト側に以下を追加しました。
proc EventKeepAlive { } {
after 100 EventKeepAlive
}
after 100 EventKeepAlive
(ここまでvwaitより前に)
100msecごとにEventKeepAlive関数が延々と呼ばれると言うだけで全く仕事をしないこの部分、これを追加するとC#と同じプロセスで作られたTclインタープリタでもちゃんと通信するようになりました。
理由、分かりません。こうやってTclのイベントを絶えず使っておかないと止まってしまうようです。ここ 書いてあるスレッドが止まると言うことと原因は同じなのかなぁ。
この解決を見つけるまで.NET Frameworkのマルチスレッド処理がおかしいのではないかとかいろいろ大変でした。ただあまりに特殊なことをやっているためここに書いても同じ状況にぶつかる人がいるのか疑問ですがw
Comments