Navigation: Advanced Topics > Thread Model Documentation > Multi-Threading Programming >====== Thread Synchronization ====== | |
The Clarion runtime has a variety of built in interfaces and procedures to help you maintain synchronization between your threads. The POST and EVENT functions have always been in the Clarion language for thread synchronization. SUSPEND and RESUME functions allow you to stop and start another thread, INSTANCE allows you to get another thread's contents for a variable and the ICriticalSection, IMutex, ISemaphore and IReaderWriterLock are interfaces to objects that allow you to synchronize the processing between multiple threads and also multiple processes.
POST/EVENT
You have always been able to synchronize thread processing by posting an event from one thread to another using POST() to send the event and EVENT() to receive it. See the SUSPEND/RESUME section below for an example on using these functions to synchronize two threads.
SUSPEND/RESUME
SUSPEND allows you to stop another process. RESUME starts that process again. You can issue multiple SUSPEND calls for a thread. The same number of RESUME calls must be made for that thread to restart.
The SUSPEND procedure suspends a thread specified by the threadno parameter. If the threadno parameter is a number of an active thread, its execution is suspended and a suspending counter is incremented. Each additional SUSPEND statement issued to the same active thread will increment the suspending counter by one. Therefore, a thread that has been suspended with a given number of SUSPEND statements can only resume thread execution when an equal number of RESUME statements has been executed.
EXTREME CAUTION should be taken with MDI programs using SUSPEND, as improper use can cause program lockups. All MDI child windows have an MDI client window as a parent, and the MDI client window can send rather than post messages to its child windows.
For example, calling the inter-thread SendMessage modal function causes the calling thread (the MDI client window) to suspend activity until the called thread (the MDI Child window) returns from the call. If the called thread is suspended, we would have a program lockup.
The SUSPEND and RESUME functions can be very useful for controlling threads that are CPU intensive. For example, rebuilding keys on a file. Here is an example program that starts a BUILD of a file and allows the user to pause the build and restart it.
PROGRAM
MAP
DoBuild(STRING)
END
MyFile FILE,DRIVER('TopSpeed'),PRE(F)
Key1 KEY(F:Field1),PRIMARY
Key2 KEY(F:Field2)
Key3 KEY(F:Field3, Field4)
RECORD
Field1 LONG
Field2 STRING(20)
Field3 STRING(20)
Field4 STRING(20)
END
END
BuilderWin WINDOW('Building File'),AT(,,81,22),GRAY
BUTTON('Suspend Build'),AT(2,3,75,14),USE(?Button)
END
AllDone EQUATE(500H)
Building BYTE
ThreadID SIGNED,AUTO
CODE
OPEN(BuilderWin)
ThreadID = START(DoBuild, , THREAD())
Building = TRUE
ACCEPT
CASE EVENT()
OF AllDone
MESSAGE('Build Complete')
BREAK
OF Event:Accepted
IF ACCEPTED() = ?Button
IF Building
SUSPEND(ThreadID)
?Button{PROP:Text} = 'Resume Building'
ELSE
RESUME(ThreadID)
?Button{PROP:Text} = 'Suspend Build'
END
END
END
END
DoBuild PROCEDURE (parent)
CODE
MyFile{PROP:FullBuild} = TRUE
BUILD(MyFile)
POST(AllDone,,parent)
INSTANCE
In versions of Clarion prior to Clarion 6.0 a variable's memory location was constant regardless of which thread accessed the variable. Therefore this code would always work:
PROGRAM
MAP
AFunc()
END
GlobVar SIGNED,THREAD
Addr LONG
CODE
Addr = ADDRESS(GlobVar,1)
START(AFunc)
AFunc PROCEDURE
CODE
IF Addr <;> ADDRESS(GlobVar)
MESSAGE('Panic')
END
This sort of code was used in ABFILE.CLW to make sure the file manager matched the file it was meant to manage. To allow programs to know what variable they are really using you can now use the INSTANCE function to get the address of the variable on any thread, and most importantly on thread 1. The above code would need to be modified as follows to work in Clarion 6.0.
PROGRAM
MAP
AFunc()
END
GlobVar SIGNED,THREAD
Addr LONG
CODE
Addr = ADDRESS(GlobVar)
START(AFunc)
AFunc PROCEDURE
CODE
IF Addr <;> INSTANCE(GlobVar,0)
MESSAGE('Panic')
END