2017年3月8日水曜日

ロック中のファイルアクセス回数を低減するサンプル

昨日公開した一時保留.htaでは、ファイルに対する処理をfunction形式で受け渡して、functionを配列に入れて、ロックに成功してアクセス権を取得したら配列内の関数を一つずつ実行していました。
その配列をテキスト化したものが以下になります。

fso.Read、fso.Writeはファイルに対する読み書きを行っています。
一つの関数内で読み書きを1セット行っています。

ファイルに対する処理が三回分まとまると以下のようになります。

かなり冗長な感じです。
対象ファイルのサイズが小さければ無視して良いかもしれませんが、サイズが大きいとか、ネットワーク越しで利用していてネットワークの転送速度が遅いとかいうことになると無視できない話になってきます。
この問題を解消するサンプルを作成しました。

以下ソース
<html>
<title>lock</title>
<body style="text-align:center;overflow:hidden;padding:0px;margin:0px;">
<input id=inp style="width:100%;text-align:center">
<button id=btnL>load</button>
<button id=btnI>init</button>
<table border=1 style="width:100%;">
<tr><td width=1>A</td><td><input id=inpA style="width:100%"></td></tr>
<tr><td width=1>B</td><td><input id=inpB style="width:100%"></td></tr>
<tr><td width=1>C</td><td><input id=inpC style="width:100%"></td></tr>
</table>
<table id=tbl style="position:absolute;top:0px;left:0px;width:100%;height:100%;background-color:#ccc"><td style="text-align:center">初期化中。。少々お待ちください</td></table>
</body>
<script id=scriptCode共有用>
AXO=function(name){return new ActiveXObject(name)}
shell = AXO('WScript.Shell')
fso=function(){
var fs=AXO('Scripting.FileSystemObject')
return {
AXO:fs,
gsf2:fs.GetSpecialFolder(2), // Z:\TEMP
Read:function(path, swCreate, swUnicode){
if(!fs.FileExists(path)){return}
with(fs.OpenTextFile(path, 1, swCreate, swUnicode)){ var str=AtEndOfStream ? '' : ReadAll(); Close() }
return str
},
Write:function(path,str,swUnicode,sw上書きしない){ with(fs.CreateTextFile(path, !sw上書きしない, swUnicode)){ Write(str); Close() } },
Lock:function(path){
var pathL=path+'.lock', ws, wsn=AXO('WScript.Network')
try{
ws=fs.CreateTextFile(pathL)
ws.Write(wsn.computerName+'\r\n'+wsn.userName)
return {Close:function(){ ws.Close(); fs.DeleteFile(pathL) }}
}
catch(e){
return this.Read(pathL)
}
}
}
}()
PID=function(){
// プロセス起動中にPIDが更新されることは無いので一度取得したら取得用関数は不要なのでPIDだけ保持する。
var f=function(){
// 子プロセスをcscriptにすると黒い画面が一瞬表示されてしまう。
// 「//B」オプションを付けないとwscriptの設定画面が表示されてしまう。
var childPID = shell.Exec('wscript //B').ProcessID
// 子の親(自身)のPIDを特定する
var res = new Enumerator(GetObject("winmgmts:root\\CIMV2").ExecQuery("SELECT * FROM Win32_Process where ProcessID="+childPID))
return res.atEnd() ? (void 0) : res.item().ParentProcessID
}
// 1度目で取得に成功すればその値を返し、ダメなら2回目の処理を実行する。
return f() || f()
}()
</script>
<script>
resizeTo(200,200)
inp.value = fso.gsf2+'\\lockTest.txt'
有ったら消す = function(path){ fso.AXO.FileExists(path) && fso.AXO.DeleteFile(path) }
obj = null
loadObj=function(){
try{ eval('obj = '+fso.Read(inp.value)) }
catch(e){ return }
inpA.value = obj.A
inpB.value = obj.B
inpC.value = obj.C
return obj
}
btnL.onclick=function(){
var path=inp.value
if(!fso.AXO.FileExists(path)){return alert('ファイルがありません')}
if(!loadObj()){ return alert('ファイルが破損しています') }
}
btnI.onclick=function(){
tbl.style.display = 'block'
if(loadObj()){return tbl.style.display = 'none'}
wsf.negoWrite(inp.value, function(path){ fso.Write(path, "{A:'', B:'', C:''}") }, function(){
tbl.style.display = 'none'
btnL.onclick()
})
}
onload=function(){ btnI.onclick() }
inpA.onchange=inpB.onchange=inpC.onchange=function(){
var fun=function(path, sw連結){
if(sw連結){return ['prop', 'VVV']}
eval('o = '+fso.Read(path))
var arr=this, ret, f=function(){}
for(var i=0,L=arr.length;i<L;i++){
ret = arr[i](0,true)
o[ret[0]] = ret[1]
arr[i] = f
}
fso.Write(path, "{A:'"+o.A.replace(/'/g,"\'")+"', B:'"+o.B.replace(/'/g,"\'")+"', C:'"+o.C.replace(/'/g,"\'")+"', 回数:"+((o.回数||0)+1)+"}")
}
return function(){
var chr=this.id.charAt(3), obj
wsf.negoWrite(inp.value, (''+fun).replace(/prop/,chr).replace(/VVV/,this.value.replace(/'/g,"\'")), loadObj)
}
}()
wsf=function(){
var pathDQ=function(path){return 0<path.indexOf(' ') ? ('"'+path+'"') : path }
var getTime=function(dat){return (dat || (new Date())).getTime()}
return {
newWSF:function(fun){
var path=fso.gsf2+'\\'+getTime()+'.wsf'
fso.Write(path, ['<-job>',scriptCode共有用.outerHTML,'<-script>','!'+fun+'()','<-/script>','<-/job>'].join('\r\n').replace(/<-/g,'<'))
return path
},
終了後ファイル削除:function(path){
var m=arguments.callee
if(!m.wsf){
m.wsf = this.newWSF(function(){
arg = WScript.Arguments
PID = arg(0)
path = arg(1)
while(true){
WScript.Sleep(1000)
var res = new Enumerator(GetObject("winmgmts:root\\CIMV2").ExecQuery("SELECT * FROM Win32_Process where ProcessID="+PID))
if(res.atEnd()){break}
}
arr = fso.Read(path).split('\r\n')
for(i=0,L=arr.length;i<L;i++){ fso.AXO.DeleteFile(arr[i]) }
})
fso.Write(m.path = m.wsf+'.list', [m.wsf, m.path].join('\r\n'))
shell.Run('cmd /C '+pathDQ(m.wsf)+' '+PID+' '+pathDQ(m.path), 0)
}
var arr=fso.Read(m.path).split('\r\n')
arr.push(path)
fso.Write(m.path, arr.join('\r\n'))
},
negoWrite:function(path, fun, fun終了後){
// path…整合性を維持したいファイルのpath
// fun …ロック成功したら「fun(path)」の形で実行される関数
// fun終了後…上記funの完了をhta側で検知したら実行される関数。
// ただしwindow.onbeforeunloadが発生した場合はこの処理は省略される(実行前にhtaが終了してしまう)ので
// ファイルの後始末などはこの関数では行わないこと。(hta画面上の表示内容変更などを行うための関数)
var m=arguments.callee
if(!m.wsf){
m.wsf = this.newWSF(function(){
arg = WScript.Arguments
path = arg(0)
pathFunc = arg(1)
pathLock = path+'.lock'
wsn = AXO('WScript.Network')
eval('arrF = '+fso.Read(pathFunc))
while(true){
try{
lock = fso.AXO.CreateTextFile(pathLock)
lock.Write(wsn.computerName+'\r\n'+wsn.userName)
break
}
catch(e){ WScript.Sleep(1000 * 10 * Math.random()) }
}
for(i=0,L=arrF.length;i<L;i++){ arrF[i](path) }
lock.Close()
try{
fso.AXO.DeleteFile(pathFunc)
fso.AXO.DeleteFile(pathLock)
}catch(e){}
WScript.Echo('OK')
})
this.終了後ファイル削除(m.wsf)
m.obj={}
}
// pathに対するfunを一時的に保管しておくための配列が未作成なら作成する。
if(!m.obj[path]){ m.obj[path] = [] }
m.obj[path].push(fun)
m.obj[path].保留開始 = getTime()
var 保留期間 = 1000 * 10
var fun期間経過後=function(){
// 保留期間中に保留開始時刻が更新された場合は、更新時に新しいsetTimeoutが実行されているので古い方は終了する。
if(getTime() < (m.obj[path].保留開始+保留期間)){return}
var pathF=fso.gsf2+'\\fun'+getTime()+'.js', pathO=pathF+'.txt'
var リダイレクト = fun終了後 ? (' > '+pathDQ(pathO)) : ''
fso.Write(pathF, '['+m.obj[path]+']')
shell.run('cmd /C cscript '+pathDQ(m.wsf)+' '+pathDQ(path)+' '+pathDQ(pathF)+リダイレクト, 0)
if(!fun終了後){return}
var objIV=setInterval(function(){
if(!fso.AXO.FileExists(pathO)){return}
try{fso.AXO.DeleteFile(pathO)}catch(e){return}
fun終了後()
clearInterval(objIV)
},1000)
}
setTimeout(fun期間経過後, 保留期間)
// ウィンドウのonbeforeunloadイベントが発生したら即時実行されるようにしておく。
window.attachEvent('onbeforeunload', function(){
m.obj[path].保留開始 = fun終了後 = 0
fun期間経過後
})
}
}
}()
</script>
</html>


前verから変更した箇所は以下
inpA.onchange=inpB.onchange=inpC.onchange=function(){
var fun=function(path, sw連結){
if(sw連結){return ['prop', 'VVV']}
eval('o = '+fso.Read(path))
var arr=this, ret, f=function(){}
for(var i=0,L=arr.length;i<L;i++){
ret = arr[i](0,true)
o[ret[0]] = ret[1]
arr[i] = f
}
fso.Write(path, "{A:'"+o.A.replace(/'/g,"\'")+"', B:'"+o.B.replace(/'/g,"\'")+"', C:'"+o.C.replace(/'/g,"\'")+"', 回数:"+((o.回数||0)+1)+"}")
}
return function(){
var chr=this.id.charAt(3), obj
wsf.negoWrite(inp.value, (''+fun).replace(/prop/,chr).replace(/VVV/,this.value.replace(/'/g,"\'")), loadObj)
}
}()


動作結果

functionの文字数は増していますが、ファイルへのアクセス回数は配列全体でReadもWriteも一回ずつに低減されています。

0 件のコメント:

コメントを投稿