Updated 11.11.05 see UPDATES comments.
------------------
Michael Mattias
Tal Systems Inc.
Racine WI USA
mailto:[email protected][email protected]</A>
www.talsystems.com
[This message has been edited by Michael Mattias (edited November 11, 2005).]
Code:
' edipal_Event_test.bas ' COMPILER: PB/CC 3.03 ' AUTHOR: Michael Mattias Racine WI ' COPYRIGHT: Placed in public domain by author ' DATE: 11.10.05 ' USE: Instead of trying to use GLOBAL variables to force a premature exit to one or ' more thread functions, Windows user-defined events may be used. ' At end of a program or any other time when desired, you may quickly terminate ' the operation of any active worker threads. ' UPDATES ' 11.11.05 Added SLEEP 500 In function SignalAllWaiting Threads. This cuts the ' number of 'useless' OpenEvent/SetEvent/CloseHandle cycles and makes the ' 'nthreadReleased' variable more reasonable, albeit not a count of number ' if threads ' Corrected typo in comment "WaitForSingleObjectT" should be "WaitForSingleObject" ' TO RUN DEMO: ' 1. Compile and execute ' 2. Press 's' any number of times to start that many worker threads. ' 3. At desired point, press "f" to force all threads to exit their loops ' 4. Repeat as necessary ' 5. press 'x' to end the demo. An additional keystroke will be required to end the program ' ------------------------------------------------------------------------------------------ #COMPILE EXE #INCLUDE "WIN32API.INC" #IF NOT %DEF(%INVALID_HANDLE_VALUE_LONG) %INVALID_HANDLE_VALUE_LONG = -1& #ENDIF ' --------------------------------- ' Shared/Common Event Name ' --------------------------------- $EVENT_NAME_DEALLOCATE_EXIT = "EDIPAL25_DEALLOCATE_EXIT" FUNCTION PBMAIN() AS LONG CALL UserInputFunction STDOUT "WinMain End of Job, any key" WAITKEY$ END FUNCTION ' --------------------------------------------------------- ' SIGNAL ANY WORKER THREADS CURRENTLY EXECUTING TO EXIT NOW ' --------------------------------------------------------- FUNCTION SignalAllWaitingThreads () AS LONG ' LOCAL hEvent AS LONG, dwAccess AS DWORD, bInheritHandle AS LONG, szName AS ASCIIZ * 64 LOCAL nThreadReleased AS LONG dwAccess = %EVENT_ALL_ACCESS bInheritHandle = %FALSE szName = $EVENT_NAME_DEALLOCATE_EXIT DO hEvent = OpenEvent (dwAccess, bInheritHandle, szName) IF ISTRUE hEvent THEN ' we were able to get a handle, meaning event is in progress 'somewhere' SetEvent hEvent ' which was created as auto-reset, so it will unsignal itself ' after releasing one thread CloseHandle hEvent SLEEP 500 ' allow windows to switch to another thread and release it INCR nThreadReleased ELSE ' could not open the event, therefore no thread is using it anymore EXIT DO END IF LOOP STDOUT "" STDOUT USING$ ("Signalled # threads before exiting", nThreadReleased) STDOUT "" ' ****************************************************************************** ' NOTE: ' The 'nThreadReleased' number is always displayed incorrectly except when zero. ' I have no idea why that is. ' But since I don't need that number, I don't care. We'll see what we get with the ' other (PB/Win 7.02)compiler. ' 11.11.05 found problem. OpenEvent/Setevent/CloseHandle does not force a thread ' switch, so waiting threads are not released; Net effect is that OpenEvent/SetEvent/ ' Closehandle does exactly nothing. Added SLEEP to 'encourage' Windows to switch to ' another thread and release it from wait state. ' ****************************************************************************** END FUNCTION FUNCTION UserInputFunction () AS LONG LOCAL action AS STRING LOCAL nLoop AS LONG, hThread AS LONG, iresult AS LONG nLoop = 10 ' number of iterations each thread function should perform if not ' prematurely terminated DO STDOUT "(S)tart thread, (F)orce Exit All, E(x)it test loop" action = LCASE$(WAITKEY$) STDOUT "Action===>" & UCASE$(action) SELECT CASE action CASE "s" ' start a new thread THREAD CREATE ThreadFunction (nLoop) SUSPEND TO hThread SetThreadPriority hThread, %THREAD_PRIORITY_LOWEST THREAD RESUME hThread TO iResult ' can't step on hThread here ' --------------------------------------------------------------------- ' IN REAL LIFE, ONE SHOULD 'WaitforSingleObject' on a readyEvent here ' SLEEP is *NOT* an acceptable substitution. ' -------------------------------------------------------------------- SLEEP 500 THREAD CLOSE hThread TO hThread ' we do not need the thread handle CASE "f" ' force all threads to exit now CALL SignalAllWaitingThreads CASE "x" ' Just exit this test procedure. We really should signal all ' waiting threads here but this was an escape whilst developing. EXIT DO CASE ELSE STDOUT Action & " is not a valid keystroke here" END SELECT LOOP END FUNCTION FUNCTION ThreadFunction (BYVAL iMax AS LONG) AS LONG LOCAL iWait AS LONG, bWaitAll AS LONG, nLoop AS LONG, MaxLoop AS LONG LOCAL hEVent() AS LONG LOCAL lpAttributes AS DWORD, bManualReset AS LONG, bInitialState AS LONG, pszName AS ASCIIZ PTR LOCAL szName AS ASCIIZ * 64 LOCAL dwAccess AS DWORD, bInheritHandle AS LONG LOCAL tid AS LONG tid = THREADID REDIM hEvent(1) ' ************************************************************************************ ' WFMO NOTE: When more than one object is signalled and bWaitAll is FALSE, WFMO returns ' the smallest index which was signalled. ' Since our function _always_ has the unnamed continue event signalled, our EXIT event ' should be at index 0. ' ******************************************************************************************** ' -------------------------------------------------------------------------------------------- ' Get a Handle to the Exit Event as index zero. If the event does not already exist, create it ' -------------------------------------------------------------------------------------------- dwAccess = %EVENT_ALL_ACCESS bInheritHandle = %FALSE szName = $EVENT_NAME_DEALLOCATE_EXIT hEvent(0) = OpenEvent (dwAccess, bInheritHandle, szName) IF ISFALSE hEvent(0) THEN ' the open failed, meaning no other dealloc thread is running STDOUT USING$("tid # could not open exit event, creating new", tid) lpAttributes = %NULL ' default security bManualReset = %FALSE ' If false, system will set to unsignalled on each statisfied wait bInitialState = %FALSE ' I want it to detect signalled state hEvent(0) = CreateEvent (BYVAL lpAttributes, bManualReset, bInitialState, szName) ELSE STDOUT USING$("tid # opened existing exit event", tid) END IF ' ******************************************************************* ' Create an unnamed event which initially is signalled as index 1 ' Because this event is signalled, it lets the loop continue when ' nothing else' has occured... which is what we want. ' ******************************************************************* lpAttributes = %NULL ' default security bManualReset = %TRUE ' If false, system will set to unsignalled on each statisfied wait ' we do not want this bInitialState = %TRUE ' I want it to be signalled pszName = %NULL ' unnamed event (don't need a name here) hEvent(1) = CreateEvent (BYVAL lpAttributes, bManualReset, bInitialState, BYVAL pszName) ' loop driver and WFMO params: bWaitAll = %FALSE nLoop = 0 maxLoop = iMax ' ------------------------------------------------------------------- ' THIS IS THE POINT WHERE A 'READY EVENT' SHOULD BE SIGNALLED TO ' RELEASE THE CALLING THREAD .. *AFTER* THE THREAD HAS OBTAINED ITS ' HANDLE TO THE SHARED 'EXIT' EVENT ' FOR THIS DEMO, WE JUST 'SLEEP' IN THE CALLING THREAD. ' THIS IS *NOT* A RECOMMENDED SUBSTITUTE FOR A 'READY EVENT.' ' ------------------------------------------------------------------- DO WHILE nLoop <= MaxLoop ' do a maxumim number of times. FOR/NEXT would work here, too. iWait = WaitForMultipleObjects (BYVAL 2&, BYVAL VARPTR(hEvent(0)), bWaitAll, %INFINITE) SELECT CASE AS LONG iWait CASE %WAIT_OBJECT_0 ' first event (the forced exit) satisfied wait. STDOUT USING$( "TID # FORCED EXIT Signalled with nLoop #", tid, nLoop) EXIT DO CASE %WAIT_OBJECT_0 + 1 ' second event (continue) satisfied wait INCR nLoop STDOUT USING$("TID # Executing loop # code" , tid, nLoop) SLEEP 2000 ' <<< THE REAL WORK GETS DONE HERE END SELECT LOOP STDOUT "Exited loop: Closing handles and exiting thread function" CloseHandle hEvent(0) CloseHandle hEvent(1) END FUNCTION ' *** END OF FILE ***
Michael Mattias
Tal Systems Inc.
Racine WI USA
mailto:[email protected][email protected]</A>
www.talsystems.com
[This message has been edited by Michael Mattias (edited November 11, 2005).]
Comment