UWSCのスレッドは自在に操れない
以前、以下のような記事を書いた。
junjun777.hatenablog.com
先日、とらさんから強制終了できないか、と相談を受けて機能追加してみたのだけど、、、。
ダメだった。
ま、スレッドを強制終了なんてしちゃいけないんですよ!
以下、その詳細である。
スクリプト
まあさ、TerminateThreadすればいいじゃん、って思うでしょ。
で、さくっと書いてみるわけです。
Thread.uws
OPTION EXPLICIT IFB GET_UWSC_NAME = "Thread.uws" THEN // CPU情報取得 DIM cpus, packs = Thread.GetCpuInfo(cpus) DIM i FOR i = 0 TO LENGTH(cpus) - 1 PRINT "cpu" + i + " core" + cpus[i] NEXT FOR i = 0 TO LENGTH(packs) - 1 PRINT "cpu" + i + " same package core" + packs[i] NEXT // プロセスのAffinityMask取得 Thread.GetAffinityMask(i) PRINT "process affinity mask:0x" + REPLACE(FORMAT(i, 8, -1), " ", "0") // スレッド操作用に選択項目定義 FOR i = 0 TO LENGTH(cpus) - 1 cpus[i] = "cpu" + i NEXT i = LENGTH(cpus) + 4 RESIZE(cpus, i) cpus[i] = "End" cpus[i-1] = "Terminate" cpus[i-2] = "Resume" cpus[i-3] = "Suspend" cpus[i-4] = "free cpu" // スレッド操作。ビジーループなので、タスクマネージャーと見比べてね DIM name = "sample", loop = TRUE Thread.Start(name, "SampleFunc(<#DBL>" + name + "<#DBL>)") WHILE loop i = SLCTBOX(SLCT_STR, 0, "control thread", cpus) SELECT i CASE -1, "End" loop = FALSE CASE "Terminate" PRINT i + ":" + Thread.Terminate(name) // 強制終了:失敗(0)、成功(0以外) CASE "Suspend" PRINT i + ":" + Thread.Suspend(name) // 一時停止:失敗(-1)、成功(前回停止カウンター) CASE "Resume" PRINT i + ":" + Thread.Resume(name) // 再開:失敗(-1)、成功(前回停止カウンター) CASE "free cpu" PRINT i + ":" + Thread.SetAffinityMask(name) // 設定:失敗(0)、成功(前回マスク) DEFAULT i = VAL(COPY(i, 4)) PRINT "cpu" + i + " set:" + Thread.SetAffinityMask(name, Thread.MakeAffinityMask(i)) SELEND WEND Thread.Stop(name) Thread.Dispose() // スクリプトの最後に破棄した方が良いかも ENDIF // スレッド関数サンプル PROCEDURE SampleFunc(name) // Threadにつけた名前は必ず必要 WHILE Thread.IsLoop(name) // 無限ループスレッドは、条件をIsLoopに従う //GETTIME() //PRINT G_TIME_HH2 + ":" + G_TIME_NN2 + ":" + G_TIME_SS2 //SLEEP(1) WEND MSGBOX("Thread Exit?") Thread.Exit(name) // 終了前に必ずExitを呼ぶ FEND MODULE Thread DEF_DLL GetCurrentThreadId(): DWORD: kernel32 DEF_DLL OpenThread(DWORD,BOOL,DWORD): DWORD: kernel32 DEF_DLL CloseHandle(DWORD): BOOL: kernel32 DEF_DLL SuspendThread(DWORD): DWORD: kernel32 DEF_DLL ResumeThread(DWORD): DWORD: kernel32 DEF_DLL TerminateThread(DWORD,DWORD): long: kernel32 DEF_DLL GetProcessAffinityMask(DWORD,var DWORD,var DWORD): BOOL: kernel32 DEF_DLL SetProcessAffinityMask(DWORD,DWORD): BOOL: kernel32 DEF_DLL SetThreadAffinityMask(DWORD,DWORD): DWORD: kernel32 HASHTBL _loops HASHTBL _threads DIM _hProcess PROCEDURE Thread DEF_DLL GetCurrentProcess(): DWORD: kernel32 _hProcess = GetCurrentProcess() FEND PROCEDURE Start(name, func, op=EMPTY) _loops[name] = TRUE THREAD PrivateFunc(name, func, op) FEND FUNCTION PrivateFunc(name, func, op) _threads[name] = OpenThread($63, FALSE, GetCurrentThreadId()) RESULT = EVAL(func) FEND FUNCTION Exit(name) RESULT = _threads[name, HASH_EXISTS] IFB RESULT THEN CloseHandle(_threads[name]) RESULT = _threads[name, HASH_REMOVE] ENDIF FEND FUNCTION Stop(name, max=0, bResume=TRUE) _loops[name] = FALSE // 終了待ち DIM res = 1, counter = 0 WHILE _threads[name, HASH_EXISTS] IF res > 0 AND bResume THEN res = this.Resume(name) SLEEP(0.01) IFB max > 0 THEN counter = counter + 1 IF counter > max THEN BREAK ENDIF WEND RESULT = _loops[name, HASH_REMOVE] FEND FUNCTION IsLoop(name) RESULT = FALSE IF _loops[name, HASH_EXISTS] THEN RESULT = _loops[name] FEND FUNCTION Suspend(name) RESULT = -1 IF _threads[name, HASH_EXISTS] THEN RESULT = SuspendThread(_threads[name]) FEND FUNCTION Resume(name) RESULT = -1 IF _threads[name, HASH_EXISTS] THEN RESULT = ResumeThread(_threads[name]) FEND FUNCTION Terminate(name, exitCode=-1) RESULT = 0 IF _threads[name, HASH_EXISTS] THEN RESULT = TerminateThread(_threads[name], exitCode) Exit(name) FEND FUNCTION Dispose() RESULT = LENGTH(_threads) DIM i, name FOR i = 0 TO RESULT - 1 name = _threads[i, HASH_KEY] this.Stop(name) NEXT _threads = HASH_REMOVEALL _loops = HASH_REMOVEALL FEND FUNCTION GetAffinityMask(var mask) IFB !GetProcessAffinityMask(_hProcess, mask, RESULT) THEN RESULT = 0 ENDIF FEND FUNCTION SetAffinityMask(name=EMPTY, mask=0) IF mask = 0 THEN this.GetAffinityMask(mask) IFB name = EMPTY THEN RESULT = SetProcessAffinityMask(_hProcess, mask) ELSEIF _threads[name, HASH_EXISTS] THEN RESULT = SetThreadAffinityMask(_threads[name], mask) ELSE RESULT = FALSE ENDIF FEND FUNCTION MakeAffinityMask(no) RESULT = POWER(2, no) FEND FUNCTION GetCpuInfo(var rcpus) DEF_DLL GetLogicalProcessorInformation(var DWORD[], var DWORD): BOOL: kernel32 DIM infoLen = 0, res TRY GetLogicalProcessorInformation(NULL, infoLen) res = (infoLen > 0) EXCEPT res = FALSE ENDTRY HASHTBL cpus HASHTBL cores IFB res THEN DIM infos[infoLen / 4] res = GetLogicalProcessorInformation(infos, infoLen) DIM i, core = 0, no = 0, set FOR i = 1 TO LENGTH(infos) - 6 STEP 6 SELECT infos[i] CASE 0 no = 0 WHILE infos[i-1] IF infos[i-1] MOD 2 THEN cpus[no] = core infos[i-1] = INT(infos[i-1] / 2) no = no + 1 WEND core = core + 1 CASE 1 no = 0 set = -1 WHILE infos[i-1] IFB infos[i-1] MOD 2 THEN IF set = -1 THEN set = no cores[no] = cpus[set] ENDIF infos[i-1] = INT(infos[i-1] / 2) no = no + 1 WEND SELEND NEXT ENDIF IFB LENGTH(cpus) THEN rcpus = SAFEARRAY(0, LENGTH(cpus) - 1) FOR res = 0 TO LENGTH(cpus) - 1 rcpus[res] = cpus[res] NEXT ELSE rcpus = SPLIT(EMPTY) ENDIF IFB LENGTH(cores) THEN RESULT = SAFEARRAY(0, LENGTH(cores) - 1) FOR res = 0 TO LENGTH(cores) - 1 RESULT[res] = cores[res] NEXT ELSE RESULT = SPLIT(EMPTY) ENDIF FEND ENDMODULE
やってみてもらえるとわかるんですが、TerminateするとUWSCが止められなくなります。
これ多分、UWSCはUWSCでスレッドを管理していて、それが片付かないから終わらないんでしょうね。
ま、止めたいなら、Suspendくらいにしてください。