UWSCで排他制御
以前、公式掲示板に「UWSCの排他制御をJavaのsyncronizedのようにできればよいのに」といった書き込みがあった。
ので、考えてみた。
単純なイベントなりでの排他制御の場合、最初に待ちに入ったスレッドが次に実行されるわけではない。
このモジュールでは、あわせてそこも解決してみた。
スクリプト
Stackモジュールに依存しています。
UWSCでスタックとキュー処理 - じゅんじゅんのきまぐれ
Synchro.uws
OPTION EXPLICIT CALL Stack IFB GET_UWSC_NAME = "Synchro.uws" THEN DIM i FOR i = 0 TO 9 THREAD TestSynchroThread(i) NEXT WHILE Synchro.Count() // 排他処理中の件数取得 SLEEP(0.1) WEND SLEEP(0.1) // 排他処理が終わって即終了して良いなら、いらない Synchro.Dispose() ENDIF PROCEDURE TestSynchroThread(num) DIM i PRINT num + " Start" FOR i = 0 TO 3 PRINT num + " Start " + i + " " + Synchro.Start() SLEEP(1) // ここ排他処理 PRINT num + " End " + i + " " + Synchro.End() NEXT PRINT num + " Fin!" FEND MODULE Synchro DIM _loop = FALSE, _event = NULL, _sync = NULL PROCEDURE Synchro IF _sync <> NULL THEN Stack.CloseHandle(_sync) _sync = Stack.CreateEventW(NULL, FALSE, TRUE, NULL) IF _event <> NULL THEN Stack.CloseHandle(_event) _event = Stack.CreateEventW(NULL, FALSE, TRUE, NULL) FEND PROCEDURE Dispose() DIM ret = Stack.Pop() WHILE ret <> EMPTY Stack.CloseHandle(ret) ret = Stack.Pop() WEND Stack.Dispose() IF _event <> NULL THEN Stack.CloseHandle(_event) _event = NULL IF _sync <> NULL THEN Stack.CloseHandle(_sync) _sync = NULL FEND PROCEDURE _StartController() _loop = Stack.Size() WHILE _loop Wait(_event) Stack.SetEvent(Stack.Top()) _loop = Stack.Size() WEND FEND FUNCTION Wait(event, timeout=$7FFFFFFF, waitUnit=100) RESULT = Stack.WAIT_TIMEOUT WHILE RESULT = Stack.WAIT_TIMEOUT AND timeout > 0 RESULT = Stack.WaitForSingleObject(event, waitUnit) timeout = timeout - waitUnit WEND FEND FUNCTION Start() DIM event = Stack.CreateEventW(NULL, FALSE, FALSE, NULL) Stack.Push(event) IFB !_loop THEN Wait(_sync) IF !_loop THEN THREAD _StartController() Stack.SetEvent(_sync) ENDIF RESULT = Wait(event) Stack.CloseHandle(event) FEND FUNCTION End() RESULT = _event <> NULL IF RESULT THEN RESULT = Stack.SetEvent(_event) FEND FUNCTION Count() RESULT = Stack.Size() DIM res = Stack.WaitForSingleObject(_event, 0) IFB res = 0 THEN Stack.SetEvent(_event) ELSEIF res = Stack.WAIT_TIMEOUT THEN RESULT = RESULT + 1 ENDIF FEND ENDMODULE
使い方
排他制御したい場所を、「Synchro.Start()」と「Synchro.End()」で挟めばOK。
全て終わったら、「Synchro.Dispose()」すること。
課題
排他制御が1種類しか使えない。
Stackモジュールを複数対応すれば、こちらも同様にやることは可能。