I have a need to serialize work in multiple threads or functions that can be simultaneously called by multiple threads (e.g. maintaining an embedded database). I was playing with a mutex (signaling) but also looked at using a critical section that is shared by multiple threads/functions. In my testing, the CS is MUCH faster that the mutex. In the target application, synchronized operations (e.g. create, read, write, view, delete) are very short (usecs) but they have to be serialized. I'm trying to determined if this approach is safe to use.
Thanks,
Jerry
Code:
#COMPILE EXE #DIM ALL #INCLUDE "win32api.inc" '// We all hate global variables but they are useful for this test (and other stuff). GLOBAL mx AS LONG ' Global mutex GLOBAL cs AS CRITICAL_SECTION ' Global critical section shared by multiple threads GLOBAL gCnt AS DWORD ' Unsafe counter to tally operations GLOBAL tLoops AS LONG ' Loop in threads to simulate serialized work operations '// Threadsafe printing so we can display a few readable results. SUB tPrint (BYREF sMsg AS STRING) THREADSAFE PRINT sMsg END SUB '// This is a non-threadsafe function that represents serialized work in multiple threads. SUB DoWork(BYREF sMsg AS STRING) ' sleep 0 ' Will SLEEP force context switches? Should not be important for this test ' tPrint sMsg ' Use for very small numbers of operations INCR gCnt ' Increment the unsafe work operation counter END SUB '// This represents multiple threads or multiple functions that can be called by multiple ' threads that must serialize work on global data. Each function either waits for a ' global mutex to be signaled or waits on the availability of a global critical section. THREAD FUNCTION myThread(BYVAL tNum AS LONG) AS LONG REGISTER n AS LONG '// Randomize the starting time for each thread. SLEEP RND(1, 100) '// Loop tLoops times doing some simple operation. FOR n = 1 TO tLoops EnterCriticalSection cs ' WaitForSingleObject mx, 999999 DoWork STR$(tNum) & STR$(n) ' Simlulate some work by calling a non-threadsafe function ' ReleaseMutex mx LeaveCriticalSection cs NEXT n tPrint "Thread" & STR$(tNum) & " finished." END FUNCTION FUNCTION PBMAIN () AS LONG LOCAL tNum AS LONG ' Number of worker threads to start LOCAL tCnt AS LONG ' Number of work operations to do in each thread LOCAL ret AS LONG '// Set loop params for number of work operations. tCnt = 10 tLoops = 50000 PRINT "Expected safe work operations =" tCnt * tLoops '// Use random delays in the working threads. RANDOMIZE TIMER '// Initialize global synchronization vars. InitializeCriticalSection cs mx = CreateMutex ("", 0, "") '// Start the working threads. Pass thread number to each for reporting. PRINT PRINT "Press key to get results after threads have completed..." PRINT FOR tNum = 1 TO tCnt THREAD CREATE myThread(tNum) TO ret THREAD CLOSE ret TO ret RESET ret NEXT n '// After threads complete, press a key to get results. WAITKEY$ PRINT PRINT "Final work operation count =" gCnt Exitmain: WAITKEY$ END FUNCTION
Jerry
Comment