あるリソース(メモリでもファイルでもIOでも何でも良い)に対して
1.値を取得する。
2.取得した値に1を足す。
3.足した結果を元に戻す。
と言う処理を行う時、初期値0で3つのプロセスが何の対策もせずにこれらを実行した場合は、最終的な結果として1〜3までのどんな値も取りえる事になる。こういう処理は往々にして何かの数を数える処理なので、この結果は期待通りであるとは言えない。これを期待通りに実行するためには、何らかのロック処理を行って、この一連の処理が同期的に実行される様にする必要がある。
今まで JavaScript でこういうことを特に気にせず使ってきたが、JavaScript でも非同期処理を良く使う様になってきた。昔からある setInterval/setTimeout 系の定期実行処理、AJAX で使う XmlHttpRequest.onreadychange の処理、ボタンクリック時の処理等である。これらが複雑に組み合わさると、同期機構を持たない JavaScript では厄介な事態になりそうだ。
しかし、本当にこれらが非同期に実行されるのかを調べて見る必要はあるだろう。かつての Windows の様に、誰かが制御を返さないかぎり他の実行フローは処理されないと言う(確か協調的マルチタスキングと呼んでいた様な...)、本当の擬似マルチタスクである可能性もあるからだ。
とりあえず実験してみよう。
var shared_result = ""; // 共有リソース
function synctest1() {
var reader = new Lajaxs.HttpConnection(document.location.href);
for (i=0; i<1000; i++) {
var d = new Date();
result += "
synctest1 = " + d.getTime(); // 共有リソースに値を追加
reader.Get(); // 時間待ちのため同期的に自分自身を読み出しておく。
}
// この処理は10秒くらいかかる
}
function synctest2() {
var reader = new Lajaxs.HttpConnection(document.location.href);
for (i=0; i<1000; i++) {
var d = new Date();
result += "
synctest1 = " + d.getTime(); // 共有リソースに値を追加
reader.Get(); // 時間待ちのため同期的に自分自身を読み出しておく。
}
// この処理は10秒くらいかかる
}
function synctest3() {
var d = new Date();
result += "
synctest3 = " + d.getTime();
// この処理はすぐ終わる
}
と言う風に、3っの処理が共有リソースの値を変更する処理を行うとする。
これらを、
setTimeout(synctest1,500); // 0.5 秒後に実行開始
setTimeout(synctest2,1000); // 1 秒後に実行開始
document.all.button1.onclick = synctest3; // ボタンを押すと実行開始
と設定する。本当に非同期に処理されるなら、shared_result の値はこれらの処理が入り混じった値になるはずだ。それでは、実行...と。
結果は同期的だった。つまり synctest2 は synctest1 の処理が終わるまでは実行されないし、これらの処理が行われている間は button1 を押すことも出来なかった。と言う事で、コンカレント処理にありがちなリソースの共有に関しては、特に気にしなくても良いようだ。しかし、かつての協調的マルチタスキング Windows で経験したような、「ハンドラ内で時間のかかる処理をやらない」為の、色んな面倒な手法をまた使わなくてはならないかと思うと、それはそれで面倒な事だと思う。
ただこの結果は Windows IE6 で試しただけのものだ。JavaScript の仕様なのかどうかわからない(記述を見つけていない)し、各OS・ブラウザで変わるものなのかどうかもまだ調べていないので、追加の調査が必要だ。忘れない様に、ここにメモしておこう。
P.S. あ onreadychange も試していない。タイマーは window オブジェクトが発生させるので上記の様にシーケンシャルになってしまうが、もしかすると ActiveX オブジェクトの場合は、タイマー処理実行中でも割り込みがかかるかも...。今日は時間が無いから続きは後日。