View Single Post
 
Old 04-10-2004, 11:24 AM
excaliber's Avatar
excaliber excaliber is offline
Senior Contributor

* Expert *
 
Join Date: Nov 2002
Location: Ohio, USA
Posts: 1,828
Default

A big problem with multithreaded applications is data sharing. If you look at file manipulations, the I/O routines lock the target file to prevent other processes from modifying the data while the current process is modifying it. If it didn't there would be data corruption and such (one process overwrites what another just wrote, so that they conflict later). The same can happen in multithreaded applications. To prevent this, you lock your variables as well, using the SyncLock statement. As an explanation, MSDN says it best:

Quote:
Monitor objects are used to ensure that a block of code runs without being interrupted by code running on other threads. In other words, code in other threads cannot run until code in the synchronized code block has finished.

Suppose, for example, that you have a program that repeatedly and asynchronously reads data and displays the results. With operating systems that use preemptive multitasking, a running thread can be interrupted by the operating system to allow time for some other thread to run. Without synchronization, it is possible that you could get a partially updated view of the data if the object that represents the data is modified by another thread while the data is being displayed. Monitor objects guarantee that a section of code will run without being interrupted. Visual Basic .NET provides the SyncLock and End SyncLock statements to simplify access to monitor objects.
SyncLock puts an exclusive lock on the object that is passed to it, preventing other threads from accessing it. SyncLock must be passed a reference object, and a handy one is the System.Type of the class itself. Here is how it would look in our example:

Code:
Public Class FillClass Private intValue as Int32 Private intReturn(99) as Int32 Private intMultiplier as Int32 Public WriteOnly Property Value() Set(ByVal Value as Int32) intValue = Value End Set End Property Public WriteOnly Property Multiplier() Set(ByVal Value as Int32) intMultiplier = Value End Set End Property Public ReadOnly Property Return() Get Return = intReturn End Get End Property Public ReadOnly Property ReturnIndex(ByVal Index as Int32) Get Return = intReturn(index) End Get End Property Public Function Fill() SynchLock GetType(FillClass) Dim i as Int32 Dim current as Int32 current = intValue For i = 0 to 100 intReturn(i) = current current *= intMultiplier Next End SyncLock End Function End Class
For instance, if this code would be run without a SyncLock, and another thread modifies intMultiplier (by using the property Multiplier) while this thread was running, the results would be incorrect. SyncLock prevents other processes from accessing the data until the block ends.

Now, for a more complex problem using Thread.Join as mentioned earlier. Join is a method that allows you to waith till a thread completes before proceeding (blocks the current thread). It can also be passed a number as an argument. It waits that number in milliseconds, then returns true if the thread is finished, or false if not.

In this example, we are going to spawn two new threads, then make another to compute the results from the previous two.
Code:
Dim oThread(2) as System.Threading.Thread Dim oFill(2) as New FillClass 'start the first thread, setting 2 as the value oThread(0) = new Thread(AddressOf oFill(0).Fill) oFill(0).Value = 2 oFill(0).Multiplier = 2 oThread(0).Start() 'start the second, using 3 as the value oThread(1) = new Thread(AddressOf oFill(1).Fill) oFill(1).Value = 3 oFill(1).Multiplier = 2 oThread(1).Start() 'wait until the first thread is done oThread(0).Join 'waith until the second thread is done oThread(1).Join oThread(2) = new Thread(AddressOf oFill(2).Fill) oFill(2).Value = oThread(0).ReturnIndex(4) - oThread(1).ReturnIndex oFill(2).Multiplier = 2 oThread(2).Start() oThread(2).Join Dim i as Int32, retVal() as Int32 retVal = oThread(2).Return For i = 0 to UBound(retVal) Listbox1.Items.Add(retVal(i)) Next

Ok, here's what happened. First, we declare two an array of threads, and an array of classes. Next we set the first thread to the first class' function, then fill the value, and finally start it. We do the same for the next one. Then, we call the oThread(0).Join, which will block until the thread finishes its execution. We do the same for the next thread, ensuring they are both done. Now we create a new thread and set its value, which is based on the previous two results (which is why we had to wait for the threads to finish execution, so we could use their values). We run, block until its done, then display the results in a Listbox.

Conclusion
This is just a shallow look at multithreading, and will get you started for simple projects. For more advanced projects with complicated and complext multithreading, advanced synchronization is needed. This is done using things such as Wait Handles, Mutext Objects, Synch Events, Interlocked Classes and ReaderWriter Locks. Unfortunately, I'm still reading over this material and don't know enough to post a tutorial on it quite yet.
__________________
RandomIRC - Your neighborhood's friendly IRC channel (irc.randomirc.com - #code)

"Perl - The only language that looks the same before and after RSA encryption."
Reply With Quote