46
Multithread ing in VB.NET Introduction Multithreading, a very powerful technique, is essential for modern software development. Software users expect t o work with a very r esponsive program that they don’t have to wait on, which is a very reasonable demand with the processor speeds that are currently available. Enter mul tithreading. Multithreading i s the concept of having several different paths of code running at the same time. When you introduce multithreading in your applications, you immediately make programming more complicated and add design time. You must know exactly what your application and all it s threads are doing at al l times. You have to account for deadlocks, race conditions and corrupting variable values. In this arti cle we will examine the different methods in Vi sual Basic.Net to accomplish thr ead synchronization. We will learn what deadlocks and race conditions are and how to avoid these common problems with multithreading. System Requirements I will assume that you already have kn owledge of basic threading in Visual Basic.Net. You should know how to create threads and know how to do basic threading operations like Join and Sleep. A copy of Visual S tudio.Net is required t o run the code samples and see the output. The code was written with Visual Studio.Net using version 1.0 of the .Net Framework with service pack 2. Case Study Structure This case study has t hree main parts. Multithreading requires a technique called synchronization to eliminate the problems described ab ove so we will first take a  brief look at what synchronizat ion is. Then an in-depth look at all met hods available in Visual Basic.Net for synchronization will be presented where you will learn how to correctly synchronize a mult ithreaded application. After this, a look at Window’s Form

Multi Threading in VB

Embed Size (px)

Citation preview

Page 1: Multi Threading in VB

8/14/2019 Multi Threading in VB

http://slidepdf.com/reader/full/multi-threading-in-vb 1/46

Multithreading in VB.NET

Introduction

Multithreading, a very powerful technique, is essential for modern software

development. Software users expect to work with a very responsive program that they

don’t have to wait on, which is a very reasonable demand with the processor speeds that

are currently available. Enter multithreading. Multithreading is the concept of having

several different paths of code running at the same time.

When you introduce multithreading in your applications, you immediately

make programming more complicated and add design time. You must know exactlywhat your application and all its threads are doing at all times. You have to account for 

deadlocks, race conditions and corrupting variable values. In this article we will examine

the different methods in Visual Basic.Net to accomplish thread synchronization. We will

learn what deadlocks and race conditions are and how to avoid these common problems

with multithreading.

System Requirements

I will assume that you already have knowledge of basic threading in Visual

Basic.Net. You should know how to create threads and know how to do basic threading

operations like Join and Sleep. A copy of Visual Studio.Net is required to run the code

samples and see the output. The code was written with Visual Studio.Net using version

1.0 of the .Net Framework with service pack 2.

Case Study Structure

This case study has three main parts. Multithreading requires a technique

called synchronization to eliminate the problems described above so we will first take a

 brief look at what synchronization is. Then an in-depth look at all methods available in

Visual Basic.Net for synchronization will be presented where you will learn how to

correctly synchronize a multithreaded application. After this, a look at Window’s Form

Page 2: Multi Threading in VB

8/14/2019 Multi Threading in VB

http://slidepdf.com/reader/full/multi-threading-in-vb 2/46

synchronization and threading apartment styles will show the differences a programmer 

must handle between standard synchronization and visual GUI synchronization.

Synchronization

So what is thread synchronization?Imagine the following lines of code:

Dim X as Integer 

X = 1

X = X + 1

To a programmer the line X = X + 1 is a single operation. But consider this

line from a computer’s perspective. Computers use machine language, which could mean

many separate operations for each line of code. For example the line above could be broken down in to several operations, such as:Move the value of X into a register, move

the value 1 into another register, add the two registers and place the value into a third

register and finally move the added values into the memory address of the variable X.

Imagine the above situation with multiple threads trying to access the variable

X at the same time. Synchronization is the process of eliminating these kinds of errors.

Without synchronization programming, the computer could stop the first thread at any

 point in time, and let the second access the variable. If the second thread wasincrementing X by 1 also, it might finish, and then the computer resumes the original

thread that was running. This thread would restore its variable information, replacing the

new X with the old value, nullifying the work that the second thread accomplished. This

is called a race condition. These errors are very hard to find. It is best to put time in

 preventing them. .

To synchronize code, you utilize locks. A lock is a way to tell the computer 

that the following group of code should be executed together as a single operation, and

not let other threads have access to the resource that is locked until the locking code is

finished. In the case study, we will examine the different types of locks and objects that

allow locking, and discuss when to use each method. When your code can handle

multiple threads, safely, it is considered thread safe. This common term is used on code

libraries and controls to designate that they are compatible with multiple threads.

Page 3: Multi Threading in VB

8/14/2019 Multi Threading in VB

http://slidepdf.com/reader/full/multi-threading-in-vb 3/46

Synchronization also adds a new type of bug you have to watch out for,

deadlocking. Deadlocking can occur if you aren’t careful with your locking techniques.

For example, assume that we have two resources, A and B. Thread 1 calls and locks

resource A at the same time thread 2 calls and locks resource B. Thread 1 then requests

resource B and thread 2 requests resource A. This is called a deadlock. Thread 1 can’t

release resource A until it gets resource B, and thread 2 can’t release resource B until it

gets A. Nothing happens and your system can’t ever complete either of the two threads.

 Needless to say this is very bad.

The only way to avoid deadlocks is to never allow a situation that could create

one. Code both threads to allocate resources in the same order. Have thread 1 allocate A

and then B, and the same with thread 2. This way thread 2 will never start until thread 1

is finished with resource A. Then it will wait until thread 1 is finished with resource B

 before continuing, and avoid the deadlock. Another good practice is to lock resources as

late as possible. Try to avoid getting locks until you absolutely need them and then

release them as soon as possible. Next we shall take a look at all the different methods of 

thread synchronization that the common language runtime provides.

Interlocked Class

Because it is a very common programming technique, variable increment and

decrement have their own framework class, the Interlocked class. This class provides

simple thread safe methods to do some common tasks with variables. The Increment and

Decrement methods add or subtract 1 from a variable. These methods can be considered

“atomic”. This means that the operating system will consider the entire operation as one

not allowing other threads to interrupt their execution. The class is a member of 

System.Threading. To use the functions without fully qualifying the name, add an

Imports System.Threading line. I will assume that the System. Threading namespace has

 been imported for all examples in the case study.

Dim X as Integer 

X = 1

X = Interlocked.Increment(X)

Page 4: Multi Threading in VB

8/14/2019 Multi Threading in VB

http://slidepdf.com/reader/full/multi-threading-in-vb 4/46

X = Interlocked.Decrement(X)

The above code ensures that the computer will not interrupt the increment or 

decrement of the variable X.

There are two additional methods in the Interlocked class, Exchange and

CompareExchange. Let’s take a closer look at the two. The Exchange method replaces

the value of a variable with the value supplied. The second value could be a hard coded

value or a variable. Don’t let the name of the method, Exchange, confuse you though.

Only the first variable passed in the first parameter will be replaced by the second. The

method won’t really exchange the values of two variables.

Dim X as Integer = 5

Dim Y as Integer = 1

Interlocked.Exchange(X, Y)‘X now equals 1 Y is still 1

Interlocked.Exchange(X, 4) ‘X now equals 4

CompareExchange will do a comparison of two variables, and if equal, replace

the one used as the first parameterwith the supplied value.

Dim i As Integer 

i = 200

Interlocked.CompareExchange(i, DateTime. Now. Day, 200)

The above code creates a new integer and then assigns the value 200 to it. We

then call the Interlocked.CompareExchange. The method compares the variable i with

200 and since they are the same, it will replace i with DateTime. Now. Day, the current

day of the month.

The Interlocked class allows you to do basic programming techniques andmake them thread safe. Let’s examine how to do more than just basic commands now.

The Dot Net framework provides several classes, and Visual Basic. Net provides one

method to handle complete synchronization. First we will take a look at the SyncLock 

Visual Basic.Net keyword.

Page 5: Multi Threading in VB

8/14/2019 Multi Threading in VB

http://slidepdf.com/reader/full/multi-threading-in-vb 5/46

SyncLock Keyword

The SyncLock keyword (lock in C#) gives an easy way to quickly lock parts of 

code. It is a built in keyword in Visual Basic. Net now. Take a look at the following

code segment:

Dim sText as String

Dim objLock as Object = New Object()

SyncLock objLock 

sText = “Hello”

End SyncLock 

First we declare a new string, sText. Then we set up a SyncLock block to

control access to the object using another locking object, objLock. This guarantees that

only one thread at a time can set the object to the string “Hello”. A lock object must be

used or an exception will be thrown on the Exit call. If you try to use an object that has

changed since the Enter call, the Exit will fail, so you cannot lock on sText itself. The

most common use of SyncLock is to lock the entire object it is in by using the Me

keyword as the parameter of the SyncLock. This will lock the object for all threads

except the executing one. This provides a very high degree of control over the locking patterns of the object at the cost of flexibility.

Public Sub Foo()

Dim sText as String

SyncLock Me

sText= “Hello”

End SyncLock 

End Sub

Locking the entire object is usually a great waste of time and processing

 power. Other methods in the Me object that have locking code based on the Me object

won’t be accessible to any threads while in the lock. If a more flexible approach is

Page 6: Multi Threading in VB

8/14/2019 Multi Threading in VB

http://slidepdf.com/reader/full/multi-threading-in-vb 6/46

needed, a locking variable can be used. Locks can also only be obtained on reference

types. If a lock on a value type is needed, you must use a locking object as shown

 below. The code locks access to iData via a reference type, System. Object. Imagine the

locking object as a key to the code. Only one thread at a time can have the key. This

allows for much greater control over what gets locked. This method will also not lock 

the whole Me object. Other threads are free to access other methods of Me, which is

much more efficient and will reduce the possibility of deadlocks.

Public Sub Foo()

Dim iData as Integer 

Dim objLock as Object = New Object()

SyncLock objLock 

iData = 3

End SyncLock 

End Sub

One drawback to using SyncLock is that other threads must wait forever for the

lock to be released if they need the locked resource. They will never time out. If you

aren’t careful and enter an infinite loop in the locking thread, or hog resources, you can

easily create deadlocks or periods of time where nothing happens. In later sections, better 

methods of synchronization will be discussed.

Flow control statements such as GoTo cannot move the code flow into a

SyncLock block of code. The thread must execute the SyncLock keyword. Old Visual

Basic 6 error handling cannot be used from inside a SyncLock block either since it uses

exception handling internally. Since all new code should be written with exception

handling, you probably won’t run into a situation like this unless upgrading a legacy

application. I would highly recommend rewriting any legacy error handling even if the

methods aren’t used for multithreading. Neither of the following code blocks will

compile:

SyncLock Me

Page 7: Multi Threading in VB

8/14/2019 Multi Threading in VB

http://slidepdf.com/reader/full/multi-threading-in-vb 7/46

On Error GoTo Errhandle ‘won’t compile

Dim i As Integer 

i = 5

End SyncLock 

Exit Sub

Errhandle:

Or:

GoTo EnterHere‘won’t compile

SyncLock Me

EnterHere:

Dim i As Integer 

i = 5

End SyncLock 

In the next section we will examine how SyncLock works internally.

Monitor Class

To examine how SyncLock works, we have to explore a framework class, the

Monitor class. The Monitor class does exactly what it says: monitors the access to a

region of code and prevents multiple threads from entering. If you are familiar with

win32 programming using C++, Monitor is similar to a critical section. Monitor creates a

lock on an object that doesn’t allow any other threads to obtain access to the object until

released by the locking thread. These locks are on sections of memory, hence the

common name critical section. We will first see how to control access to a block of code,

 just like with the SyncLock keyword.

The Enter function of the Monitor class works just like the SyncLock keyword

and the Exit function is like the End Synclock keywords. Internally SyncLock uses the

Monitor class to implement its functionality and generates the inner Try Finally block of 

the code sample for you. Let’s look at the code now:

Page 8: Multi Threading in VB

8/14/2019 Multi Threading in VB

http://slidepdf.com/reader/full/multi-threading-in-vb 8/46

Public Sub Foo()

Dim sText as String

Dim objLock as Object = New Object()

Try

Monitor.Enter(objLock)

Try

sText = “Hello”

Finally

Monitor.Exit(objLock)

End Try

Catch e as Exception

MessageBox.Show(e.Message)

End Try

End Sub

This provides the exact same functionality that the SyncLock example abovedid. You will also notice that the Exit is contained in the finally clause of a Try Catch

Finally block. This is to ensure that Exit gets called so the thread won’t get locked

infinitely. Monitor. Enter is also called outside of the Try Catch Finally block. This is

so Monitor. Exit won’t get called if the Enter method doesn’t, as it will throw another 

exception. So why should we use Monitor, as the SyncLock keyword provides the same

functionality without the extra work of Monitor. We will examine the reasons why

Monitor should be used as we look at the other methods of Monitor.

We said earlier that the SyncLock block would wait indefinitely on the

executing thread to release the lock. The Monitor class provides a much better method to

handle this, the TryEnter method. This is the first reason why you would use Monitor 

over SyncLock. This method will allow the calling thread to wait a specific amount of 

time to acquire a lock before returning false and stopping its execution. This allows

Page 9: Multi Threading in VB

8/14/2019 Multi Threading in VB

http://slidepdf.com/reader/full/multi-threading-in-vb 9/46

graceful handling of long running threads or deadlocks. If a deadlock has occurred, you

certainly do not want to add more threads that are trying to get to the deadlocked

resource.

The default method of no parameters, will try to acquire a lock, and if unsuccessful it will return false immediately. There are also two additional overloads

that will wait for the specific number of milliseconds, or the specified TimeSpan. This

offers much more flexibility than SyncLock.

Public Sub Foo()

Dim sText As String

Dim objLock As Object = New Object()

Dim bEnteredOk As Boolean

 bEnteredOk = Monitor. TryEnter(objLock, 5000)

If bEnteredOk = True Then

sText = "Hello"

Monitor.Exit(objLock)

End If 

End Sub

This example will try to acquire a lock for five seconds. If successful the

stringis set to “Hello”.

The rest of Monitor’s methods must be examined together. The SyncLock 

keyword and the Monitor. Enter rely on putting waiting threads to sleep to stop their 

execution. This isn’t the best practice to follow as there is no way to get them to stop

waiting unless aborted. The Monitor. Wait and Monitor. Pulse allow threads to wait on

other conditions before starting. The methods will place the thread in a wait state

allowing other threads to specify when they need the waiting thread to run. An example

of this is a queue. You could have a thread that waits in an idle state until other threads

 place objects in the queue for it to work on.

Page 10: Multi Threading in VB

8/14/2019 Multi Threading in VB

http://slidepdf.com/reader/full/multi-threading-in-vb 10/46

To use the methods you first tell a thread to wait on an object with a Monitor.

Wait call like below.

Dim objLock As Object = New Object()

Dim bPulsed As Boolean

Monitor.Enter(objLock)

 bPulsed = Monitor.Wait(objLock)

If bPulsed Then

'thread was pulsed

End If 

Monitor.Exit(objLock)

The thread is automatically unlocked with the Wait call. You must be sure to

call Monitor. Exit when the thread is pulsed and done with its work, or you will have a

 block that could result in a deadlock. The first thread will wait until the pulsing thread

has released its lock. This will make the thread wait until a Monitor. Exit is called, like

the following.

Monitor.Enter(objLock)

Monitor.Pulse(objLock)

Monitor.Exit(objLock)

If the Exit call is left off a block occurs because the waiting thread cannot

obtain its lock on the object that the pulsing thread has. You must also use the same

object to lock on, and pulse from the second thread that the waiting thread used to wait

on, objLock. Also, both Wait and Pulse must be called from a locked block of code,

hence the Enter and Exit calls in the above code. You should exit immediately after 

calling Pulse to allow the first thread to perform its work, since the pulsing code has the

current lock on objLock.

The Monitor class also comes equipped with a PulseAll method. Unlike Pulse,

which will only start the next waiting thread, PulseAll removes the wait state from all

Page 11: Multi Threading in VB

8/14/2019 Multi Threading in VB

http://slidepdf.com/reader/full/multi-threading-in-vb 11/46

waiting threads and allows them to continue processing. As with the Pulse method,

PulseAll must be called from a locked block of code, and on the same object that the

original threads are waiting on.

The Monitor class will provide for most of your threading synchronizationneeds. It should be used unless a more specific task calls for the next few classes we will

examine. Here is a review of some good practices to follow when using Monitor:

Exit MUST be called the same number of times Enter is called, or a block will

occur.

Make sure that the object used to call Enter is the same object that is used to call

Exit or the lock will not be released.

Don’t call Exit before calling Enter, or call Exit more times than calling Enter or 

an exception will occur.

Place the Exit method call in a Finally block. All code that you wish to lock 

should be in the Try section of the corresponding Finally block. The Enter call should be

in its own Try block. This eliminates calling Exit if the Enter fails.

Don’t call Enter on an object that has been set to Nothing or an exception will

occur.

Don’t change the object that you use as the locking object, which brings in 7,

Use a separate locking object, and not the changing object. If you use an object

that has changed, an exception will be generated.

MethodImplAttribute

Code attributes in the Dot Net Framework can sometimes make programming

easier. The MethodImplAttribute is one example of the hundreds of different attributes

that you can use. It is in the System. Runtime. CompilerServices namespace. This

attribute is particularly interesting to synchronization because it can synchronize an entire

method with one simple command.

If you place the attribute before a function and supply the MethodImplOptions.

Synchronized enumeration in the constructor, the entire method will be synchronized

Page 12: Multi Threading in VB

8/14/2019 Multi Threading in VB

http://slidepdf.com/reader/full/multi-threading-in-vb 12/46

when called. The compiler will create output that wraps the whole function,

MySyncMethod, in a Monitor. Enter and Monitor. Exit block. When a thread starts to

enter the method it will acquire a lock. Upon exiting the method, it will release the lock.

Here is an example of using the attribute.

<METHODIMPLATTRIBUTE(METHODIMPLOPTIONS.SYNCHRONIZED)

>Private

Sub MySyncMethod()

End Sub

This attribute should only be used when an entire function needs to be

synchronized, so it is rarely used. If you can exit the synchronized block of code before

the end of the method or wait to enter it to the middle of the method, Monitor should be

used, as the attribute would waste processing cycles by locking the whole method and not

 just what needs to be synchronized.

WaitHandle, AutoResetEvent and ManualResetEvent Classes

We will now examine a MustInherit type class, WaitHandle. WaitHandle

 provides a class definition for three other classes, Mutex, ManualResetEvent and

AutoResetEvent, and provides means for your own objects to inherit synchronization

functionality. These objects allow threads to wait until classes derived from WaitHandle

are signaled. The WaitHandle derived classes add functionality over Monitor in that

threads can be programmed to wait until multiple classes are signaled. Of course, along

with more power and flexibility comes more work and chance of problems.

The two reset event classes can be used in context with Mutex to provide

similar functionality to Monitor. The major difference between Mutex and Monitor is

that Mutex can be used across processes. You can think of the two reset event classes as

 being switches. The thread cannot enter a Mutex unless its object is signaled. We will

examine them in detail next.

The AutoResetEvent class can be compared to the Monitor. Pulse method.

Imagine it as a tollbooth. Each car has to pay to go through, the signal, and then the gate

closes behind the car when it passes making the next car in line pay again. The

Page 13: Multi Threading in VB

8/14/2019 Multi Threading in VB

http://slidepdf.com/reader/full/multi-threading-in-vb 13/46

AutoResetEvent class is like this. It automatically goes back to unsignaled after being

signaled and a thread goes through, just like Monitor. Pulse. ManualResetEvent can be

described as a water hose, once open it lets everything through until you close it

yourself.

Let’s examine the AutoResetEvent in detail first. It comes equipped with two

methods to control its state, Set and Reset. Set allows one thread to acquire the lock on

the object. After allowing a thread to pass through, Reset will automatically be called,

returning the state to unsignaled.

On the first call to Set the runtime will make sure that the state of the object is

signaled. Multiple calls to Set have no effect if the state is already signaled, and it will

still allow only one thread to pass. You do not know the order of threads for each signal

either. If multiple threads are waiting on an object, you are only guaranteed that one will

get in per Set when a wait method is called.

Reset can be used to change the state of the object back to unsignaled from

signaled before a thread calls a wait method on the object. Reset will return True if it can

change the state back to unsignaled or False if it can not. It has no effect on an

unsignaled object. The code below will show how an AutoResetEvent works.

Dim WaitEvent As AutoResetEvent

WaitEvent = New AutoResetEvent(False)

Public Sub DoWork()

'do some long processing task simulate by sleeping

Thread.Sleep(5000)

WaitEvent.Set()

End Sub

Public Sub Thread2()

'we want thread 2 to run after thread1 is

'finished. It will take

Page 14: Multi Threading in VB

8/14/2019 Multi Threading in VB

http://slidepdf.com/reader/full/multi-threading-in-vb 14/46

Page 15: Multi Threading in VB

8/14/2019 Multi Threading in VB

http://slidepdf.com/reader/full/multi-threading-in-vb 15/46

The next method, WaitAll, is very useful when you have a large amount of 

work to accomplish and want to use multiple threads to accomplish it. This allows a

thread to wait on multiple objects. Once all objects in the array are signaled the waiting

thread is allowed to continue execution.

As with the WaitOne method, the no parameter method waits indefinitely while

two other methods exist to wait for a specific amount of time. The method also has the

 boolean parameter for exiting a synchronized context. Be careful when waiting infinitely

when using WaitAll. If you don’t signal all instances of the AutoResetEvent correctly as

shown below, your waiting thread will never resume.

Lets take a look at a code example of how to use WaitAll. First the form’s code:

Dim WaitAllEvents(1) As AutoResetEvent

Private Sub Button1_Click(ByVal sender As System. Object, ByVal

e As System. EventArgs) Handles Button1. Click 

Dim thread1 As Thread

Dim thread2 As Thread

‘first we create 2 threads as assign them to subs

thread1 = New Thread(AddressOf Thread1Work)

thread2 = New Thread(AddressOf Thread2Work)

‘Next our 2 AutoRresetEvent instances are created

WaitAllEvents(0) = New AutoResetEvent(False)

WaitAllEvents(1) = New AutoResetEvent(False)

thread1.Start()

thread2.Start()

‘after starting the threads we tell the main thread to

‘wait until all instances of AutoResetEvent have become

‘signaled with a call to Set()

Page 16: Multi Threading in VB

8/14/2019 Multi Threading in VB

http://slidepdf.com/reader/full/multi-threading-in-vb 16/46

WaitHandle. WaitAll(WaitAllEvents)

Console. WriteLine("All threads done exiting main thread")

thread2 = Nothing

thread1 = Nothing

End Sub

Private Sub Thread1Work()

Thread. Sleep(5000)

Console. WriteLine("Thread1 done")

WaitAllEvents(0). Set() ‘I’m done so signal my Event

End Sub

Private Sub Thread2Work()

Thread. Sleep(3000)

Console. WriteLine("Thread2 done")

WaitAllEvents(1).Set()‘I’m done so signal my Event

End Sub

 Now some code in a Module.

<MTATHREAD()>Public Sub Main()

Dim frm As Form1

frm = New Form1()

frm.ShowDialog()

End Sub

The output from the code is:

Thread2 Done

Thread1 Done

Page 17: Multi Threading in VB

8/14/2019 Multi Threading in VB

http://slidepdf.com/reader/full/multi-threading-in-vb 17/46

All threads done exiting main thread

As you can see from the output the main thread waits until all objects in its

WaitAllEvents array are signaled. Another item that is worthy to note here is the

attribute <MTATHREAD()>. This signifies that the main thread should run as amultithreaded apartment style thread and not as a single threaded apartment, which is the

default. WaitAll must be called from a thread that is an MTAThread. If not it will throw

a NotSupportedException. While done as an example above with a simple WinForm,

you should not run your main thread that opens Window’s Forms on an MTAThread.

This will cause some problems with some of the controls.

The single threaded apartment style thread model guarantees that only one

thread is accessing code at one time. In order for Windows Forms projects to work 

correctly, they must be run in a single threaded apartment. This does not mean than

worker threads cannot be created and used. We will go into more detail about Windows

Form synchronization later in the case study. Some of the other project types, such as the

Window’s service project, are by default multithreaded apartments. The MTA style will

also be discussed later. In these situations, WaitAll can be used very effectively.

The last method we will examine is WaitAny. This method waits until any one

object in the array is signaled. An example of its use could be a dictionary search

engine. The program could start two threads, the first that started with the letter A and

the second that started with the letter Z. The first match found by either thread will

terminate the others that are searching and return control to the main application. The

return of this method tells you the position of the array that was signaled. Like the other 

two methods, you can wait indefinitely or for a specific amount of time.

Let’s look at a code example.

Dim WaitAnyEvents(1) As AutoResetEvent

Private Sub Start_Click(ByVal sender As System.Object, ByVal e As System.

EventArgs)

Handles Button1.Click 

Dim Thread1 As Thread

Page 18: Multi Threading in VB

8/14/2019 Multi Threading in VB

http://slidepdf.com/reader/full/multi-threading-in-vb 18/46

Dim Thread2 As Thread

Thread1 = New Thread(AddressOf Thread1Work)

Thread2 = New Thread(AddressOf Thread2Work)

WaitAnyEvents(0) = New AutoResetEvent(False)

WaitAnyEvents(1) = New AutoResetEvent(False)

Thread1.Start()

Thread2.Start()

WaitHandle.WaitAny(WaitAnyEvents)

Console.WriteLine("One thread done exiting main thread")

End Sub

Private Sub Thread1Work()

Thread.Sleep(5000)

Console.WriteLine("Thread1 done")

WaitAnyEvents(0). Set()

End Sub

Private Sub Thread2Work()

Thread. Sleep(3000)

Console.WriteLine("Thread2 done")

WaitAnyEvents(1). Set()

End Sub

In examining the above code, we see that an array of AutoResetEvent has been

created as a form level variable so that all subroutines can access it. We have put a

command button on the form. This button is the main worker of the example. When it is

clicked, we create two new threads and assign their individual subs to run upon starting.

The subs simulate work by sleeping for a while. When done sleeping, a string is out put

Page 19: Multi Threading in VB

8/14/2019 Multi Threading in VB

http://slidepdf.com/reader/full/multi-threading-in-vb 19/46

to the debug window and the corresponding AutoResetEvent is signaled. This causes the

main thread to resume running. You should receive the following output from the

example:

Thread2 Done

One thread done exiting main thread

Thread1 done

The output shows that the main thread resumes running after the first object has

 been released. Because the main thread doesn’t abort the first thread, Thread1, it

eventually finishes outputting its string “Thread1 done”. If the other threads are no

longer needed they should be aborted manually from your main thread with a call to

Abort.

 Now let’s examine a way to signal an event and have it stay signaled, the

ManualResetEvent. This event will stay signaled no matter how many threads do a wait

method on it. This only way to change the state is to call Reset. You can use the object

to control access to data that multiple threads are waiting on. For example, we might

have two threads or more, we might not know (or care), waiting on a piece of data that

another thread is calculating. When this thread gets done with its work, we can let all

other threads in to access the data. At some later time if we determine that the data needs

to be recalculated, we can turn off the threads from accessing it. Then do our new

calculations.

Let’s look at some code now.

Private ManualWaitEvent As ManualResetEvent

Dim Thread1 As Thread

Dim sData As String

Private Sub Form1_Load(ByVal sender As System.Object,

ByVal e As System. EventArgs) Handles MyBase.Load

ManualWaitEvent = New ManualResetEvent(False)

Page 20: Multi Threading in VB

8/14/2019 Multi Threading in VB

http://slidepdf.com/reader/full/multi-threading-in-vb 20/46

Thread1 = New Thread(AddressOf ReadWork)

Thread1.IsBackground = True

Thread1.Start()

End Sub

Private Sub ReadWork()

'this method will wait until ManualWaitEvent is

'signaled

Dim i As Integer 

For i = 0 To 100

ManualWaitEvent.WaitOne()

Console.WriteLine(sData & i. ToString())

Thread.Sleep(1000)

 Next 'i

End Sub

Private Sub btnSet_Click(ByVal sender As System.Object, ByVal e AsSystem.EventArgs) Handles btnSet. Click 

sData = "Work Done: "

ManualWaitEvent.Set()

End Sub

Private Sub btnReset_Click(ByVal sender As System.Object,

ByVal e As System.EventArgs) Handles btnReset.Click 

ManualWaitEvent.Reset()

End Sub

When the form loads, we create a new instance of a ManualResetEvent in the

unsignaled state. A thread is created and started. The thread then waits until the event

Page 21: Multi Threading in VB

8/14/2019 Multi Threading in VB

http://slidepdf.com/reader/full/multi-threading-in-vb 21/46

 becomes signaled. When signaled it reads a string that we are using to represent our 

data. This is a very powerful method of controlling synchronization when you have

multiple threads. It lets you fine tune access to variables easily. You can easily switch

on and off access to the data.

Every second, the thread will output “Work Done: “ and the value of i until the

ManualWaitEvent is unsignaled by pressing the reset button. If the set button is pressed

again the thread will resume its work and continue to output data to the output window.

Every time ManualWaitEvent. WaitOne() is called, a check of the state of 

ManualWaitEvent is done. If this call were outside of the loop, all one hundred values of 

i would have been printed the first time the set button was pressed.

Also note the IsBackground call in the form load event. This makes Thread1 a

child thread to the main process thread. If the main thread is terminated, the operating

system will also terminate any background threads related to the main one. If the thread

were not a background thread, it would continue running until it was finished, even when

we closed our main thread out. If the state of ManualWaitEvent were unsignaled, the

thread would be waiting on an object that could never be signaled again since our main

form was gone. This results in the process being left in memory. This should be avoided

 by making all threads background threads, unless it is 100% necessary for the thread to

finish regardless of the state of the application. Make sure that these non-background

threads have access to any resources they need also. If termination of the main running

 program disposes of a needed resource, the thread will never finish or result in an error.

Mutex Class

The next class in our list, Mutex, can be thought of as a more powerful version

of Monitor. Like AutoResetEvent and ManualResetEvent, it is derived from

WaitHandle. An advantage of Mutex over Monitor is that you can use the methods from

WaitHandle such as WaitOne. A disadvantage is that is much slower, at about half as

fast as Monitor. Mutex is very useful when you must control access to a resource that

could be accessed through multiple processes, like a data file used by several applications

you have created. To write to the file, the writing thread must have total access to the file

throughout the operating system.

Page 22: Multi Threading in VB

8/14/2019 Multi Threading in VB

http://slidepdf.com/reader/full/multi-threading-in-vb 22/46

Page 23: Multi Threading in VB

8/14/2019 Multi Threading in VB

http://slidepdf.com/reader/full/multi-threading-in-vb 23/46

mutexFile.WaitOne()‘Wait until the file is open

Console.WriteLine("Mutex was released from another 

 process")

‘Now I know that I have explicit access to the file

‘I can write to it now.

mutexFile.ReleaseMutex()

End Sub

Let’s examine the first program. A Mutex called mutexFile is created.

Internally to the operating system, we name the mutex “Mutex Name”. This is the name

that will be used to resolve any other calls to the same mutex from any other application

that we create. On a form we have two buttons. For demonstration purposes, one button

will acquire a lock on the resource, in this case the file, using the Mutex and the other 

 button will release the lock. This simulates a long running process on the file. As with

the other synchronization classes, you should make sure to call RelaseMutex sometime

after a lock is acquired or a block on the resource will occur.

The second program is very straightforward. We create a Mutex object called

fileMutex making sure we have named it the same as in the first program, “Mutex

 Name”. If this is not done the Mutex classes will refer to different mutexes in the

operating system. Then WaitOne is called without a timeout value. This will make the

thread wait until the Mutex has been released. When the release button is clicked in the

first program, the second can continue running since it can now acquire access to the

resource. Mutex was released from another process is printed in the output window.

You can also close the first program and the lock will be released. When a thread exits

that has a Mutex lock on a resource, ReleaseMutex is automatically called for you.

In summary, remember that Monitor should be used most of the time. It is

faster than a Mutex. Mutex should only be used when you need to synchronize across

multiple processes to gain access to a common resource among several programs that you

have written. Even though Mutex allows for the wait methods where Monitor does not,

Page 24: Multi Threading in VB

8/14/2019 Multi Threading in VB

http://slidepdf.com/reader/full/multi-threading-in-vb 24/46

the other WaitHandle classes should be considered before Mutex if you need the wait

methods first.

ReaderWriterLock Object

Many times, you read data much more often than you write it. Traditional

synchronization can be overkill in these situations as it would lock resources when

threads are reading or writing to the resource. A more efficient way has been added to

the framework to handle this. The ReaderWriterLock is a synchronization class that

allows multiple threads to read a variable, but only one thread to write to it at a time.

When acquiring a lock, the write thread must also wait until all reader threads

have unlocked the object before obtaining an exclusive write lock. All readers will then

 be blocked until the writer thread releases its lock. The power of the class comes from

the fact that it will allow multiple reader locks to access the resource at the same time.

We will look first at how to acquire reader locks on an object.

Dim lData As Long = 1

Dim objLock As ReaderWriterLock 

Private Sub btnRun_Click(ByVal sender As System. Object, ByVal

e As System. EventArgs) Handles btnRun. Click 

Dim Thread1 As Thread

Dim Thread2 As Thread

objLock = New ReaderWriterLock()

Thread1 = New Thread(AddressOf Thread1Work)

Thread2 = New Thread(AddressOf Thread2Work)

Thread1.Start()

Thread2.Start()

End Sub

Private Sub Thread1Work()

Page 25: Multi Threading in VB

8/14/2019 Multi Threading in VB

http://slidepdf.com/reader/full/multi-threading-in-vb 25/46

Dim i As Integer 

For i = 1 To 10

objLock.AcquireReaderLock(1000)

Console.WriteLine(lData & " Thread 1")

Thread.Sleep(10)

objLock.ReleaseReaderLock()

 Next

End Sub

Private Sub Thread2Work()

Dim i As Integer 

For i = 1 To 10

objLock.AcquireReaderLock(1000)

Console.WriteLine(lData & " Thread 2")

objLock.ReleaseReaderLock()

 Next

End Sub

We create an instance of a ReaderWriterLock object called objLock. Then two

threads are spawned, both of which do a quick loop that writes the value of lData to the

console window ten times. The first thread also has a ten-millisecond sleep call. This

allows us to see that the second thread continues to get a reader lock on objLock even

though the first already has one. Note also that we have passed a millisecond time limit

to the methods. You must pass a timeout value to AcquireReaderLock. If you wish to

wait infinitely, use the constant Timeout. Infinite.

The output should be something similar to the following:

1 Thread 1

1 Thread 2

Page 26: Multi Threading in VB

8/14/2019 Multi Threading in VB

http://slidepdf.com/reader/full/multi-threading-in-vb 26/46

1 Thread 2

1 Thread 2

1 Thread 2

1 Thread 2

1 Thread 2

1 Thread 2

1 Thread 2

1 Thread 2

1 Thread 2

1 Thread 1

1 Thread 1

1 Thread 1

1 Thread 1

1 Thread 1

1 Thread 1

1 Thread 1

1 Thread 1

1 Thread 1

This shows that the second thread ran while the first had a ReaderLock on the

lData integer.

If needed, there is also a method IsReaderLockHeld that will return true if thecurrent thread already has a reader lock. This helps keep track of multiple locks by one

thread. For each call to AcquireReaderLock a subsequent call to ReleaseReaderLock is

required. If you do not call ReleaseReaderLock the same number of times, the reader 

lock is never fully released, never allowing a write to the resource. IsReaderLockHeld

Page 27: Multi Threading in VB

8/14/2019 Multi Threading in VB

http://slidepdf.com/reader/full/multi-threading-in-vb 27/46

can be checked to see if a reader lock is already active on the thread, and if so not acquire

another one.

 Now let’s examine how to update the variable. A writer lock can be obtained

 by calling AcquireWriterLock. Once all reader locks have been released, the method willobtain an exclusive lock on the variable. When updating the variable, all reader threads

will be locked out until ReleaseWriterLock is called. Let’s examine the code for this.

Dim lData As Long = 1

Dim objLock As ReaderWriterLock 

Private Sub btnRun_Click(ByVal sender As System. Object, ByVal

e As System.EventArgs) Handles btnRun. Click 

Dim Thread1 As Thread

Dim Thread2 As Thread

Dim Thread3 As Thread

objLock = New ReaderWriterLock()

Thread1 = New Thread(AddressOf Thread1Work)

Thread2 = New Thread(AddressOf Thread2Work)

Thread3 = New Thread(AddressOf Thread3Work)

Thread1. Start()

Thread2. Start()

Thread3. Start()

End Sub

Private Sub Thread1Work()

Dim i As Integer 

For i = 1 To 10

objLock.AcquireReaderLock(1000)

Page 28: Multi Threading in VB

8/14/2019 Multi Threading in VB

http://slidepdf.com/reader/full/multi-threading-in-vb 28/46

Console.WriteLine(lData & " Thread 1")

Thread.Sleep(100)

objLock.ReleaseReaderLock()

 Next

End Sub

Private Sub Thread2Work()

Dim i As Integer 

For i = 1 To 10

objLock.AcquireReaderLock(1000)

Console.WriteLine(lData & " Thread 2")

Thread.Sleep(100)

objLock.ReleaseReaderLock()

 Next

End Sub

Private Sub Thread3Work()

objLock.AcquireWriterLock(Timeout. Infinite)

lData = 2

Console.WriteLine("Thread 3 updated lData")

objLock.ReleaseWriterLock()

End Sub

You will notice that we have added a new thread, Thread3 and a function for it

to run. This new function acquires a writer lock on the object and then updates lData to

2. The first two threads, Thread1 and Thread2, are put to sleep for one hundred

milliseconds to allow thread three to start. When examining the output from this code,

you will see that thread three waits until threads one and two release their locks. This

Page 29: Multi Threading in VB

8/14/2019 Multi Threading in VB

http://slidepdf.com/reader/full/multi-threading-in-vb 29/46

thread three updates the variable. Thread one and two must then wait on it. As with the

reader lock, there is also a method called IsWriterLockHeld that will return true if the

current thread has a writer lock. You should get output similar to below:

1 Thread 1

1 Thread 2

Thread 3 updated lData

2 Thread 2

2 Thread 1

2 Thread 2

2 Thread 1

2 Thread 2

2 Thread 1

2 Thread 2

2 Thread 1

2 Thread 2

2 Thread 1

2 Thread 2

2 Thread 1

2 Thread 2

2 Thread 1

2 Thread 2

2 Thread 1

2 Thread 2

2 Thread 1

Page 30: Multi Threading in VB

8/14/2019 Multi Threading in VB

http://slidepdf.com/reader/full/multi-threading-in-vb 30/46

Another useful method of the ReaderWriterLock class is the

UpgradeToWriterLock method. This method allows a reader lock to become a writer 

lock to update the data. Sometimes it is useful to check the value of a data item to see if 

it should be updated. Acquiring a writer lock to check the variable is wasted time and

 processing power. By getting a reader lock first other reader threads are allowed to

continue accessing the variable until you determine an update is needed. Once the update

is needed, UpgradeToWriterLock is called locking the resource for update as soon as it

can acquire the lock. Just like AcquireWriterLock, UpgradeToWriterLock must wait

until all readers accessing the resource are done. Now let’s look at the code.

Dim lData As Long = 1

Dim objLock As ReaderWriterLock 

Private Sub btnRun_Click(ByVal sender As System.Object, ByVal

e As System. EventArgs) Handles btnRun.Click 

Dim Thread1 As Thread

Dim Thread2 As Thread

objLock = New ReaderWriterLock()

Thread1 = New Thread(AddressOf Thread1Work)

Thread2 = New Thread(AddressOf Thread2Work)

Thread1.Start()

Thread2.Start()

End Sub

Private Sub Thread1Work()

Dim i As Integer 

For i = 1 To 10

objLock.AcquireReaderLock(1000)

If lData = i Then

Page 31: Multi Threading in VB

8/14/2019 Multi Threading in VB

http://slidepdf.com/reader/full/multi-threading-in-vb 31/46

objLock.UpgradeToWriterLock(Timeout. Infinite)

lData = i + 1

Console.WriteLine("lData is now " & lData)

End If 

Thread.Sleep(20)

objLock.ReleaseReaderLock()

 Next

End Sub

Private Sub Thread2Work()

Dim i As Integer 

For i = 1 To 10

objLock.AcquireReaderLock(1000)

Console.WriteLine(lData & " Thread 2")

Thread.Sleep(20)

objLock.ReleaseReaderLock()

 Next

End Sub

In this example, we have changed thread one to examine the value of lData

after acquiring a reader lock. If the value of lData is equal to the looping variable of i

(which it always is in our example) then it tries to obtain a writer lock by calling

UpgradeToWriterLock. Nothing special is required to release the writer lock once

finished with it. The normal ReleaseReaderLock will release the upgraded writer lock, or 

calling DowngradeFromWriterLock can be used also which will be discussed next. The

output should be something similar to the following:

lData is now 2

2 Thread 2

Page 32: Multi Threading in VB

8/14/2019 Multi Threading in VB

http://slidepdf.com/reader/full/multi-threading-in-vb 32/46

lData is now 3

3 Thread 2

lData is now 4

4 Thread 2

lData is now 5

5 Thread 2

lData is now 6

6 Thread 2

lData is now 7

7 Thread 2

lData is now 8

8 Thread 2

lData is now 9

9 Thread 2

lData is now 10

10 Thread 2

lData is now 11

11 Thread 2

Opposite of UpgradeToWriterLock we can also use

DowngradeFromWriterLock. Like its name suggests the method will make a writer lock 

turn to a reader lock. To use the function, you must pass it a LockCookie. This cookie

can be generated from UpgradeToWriterLock. Because of the LockCookie requirement,

you may only use DowngradeFromWriterLock on the same thread that

UpgradeToWriterLock is called.

One advantage of DowngradeFromWriterLock is that the call returns

immediately and will not block the thread at all. This happens because it can only be

Page 33: Multi Threading in VB

8/14/2019 Multi Threading in VB

http://slidepdf.com/reader/full/multi-threading-in-vb 33/46

called from a thread that has a writer lock on an object. This means that no other thread

can have a lock; hence the method knows that it is the only thread active on the object. If 

read access is still required to the resource this method will eliminate the need to

reacquire a read lock on the thread. If read access is not required anymore, simply use

ReleaseReaderLock as shown above. Let’s examine some code now.

Dim lData As Long = 1

Dim objLock As ReaderWriterLock 

Private Sub btnRun_Click(ByVal sender As System.Object, ByVal

e As System.EventArgs) Handles btnRun. Click 

Dim Thread1 As Thread

Dim Thread2 As Thread

objLock = New ReaderWriterLock()

Thread1 = New Thread(AddressOf Thread1Work)

Thread2 = New Thread(AddressOf Thread2Work)

Thread1.Start()

Thread2.Start()

End Sub

Private Sub Thread1Work()

Dim i As Integer 

Dim objCookie As LockCookie

For i = 1 To 10

objLock.AcquireReaderLock(1000)

If lData = i Then

objCookie = objLock.UpgradeToWriterLock(Timeout. Infinite)

lData = i + 1

Page 34: Multi Threading in VB

8/14/2019 Multi Threading in VB

http://slidepdf.com/reader/full/multi-threading-in-vb 34/46

Console.WriteLine("lData is now " & lData)

objLock.DowngradeFromWriterLock(objCookie)

Console.WriteLine("Downgraded lock")

End If 

Thread.Sleep(20)

objLock.ReleaseReaderLock()

 Next

End Sub

Private Sub Thread2Work()

Dim i As Integer 

For i = 1 To 10

objLock.AcquireReaderLock(1000)

Console.WriteLine(lData & " Thread 2")

Thread.Sleep(20)

objLock.ReleaseReaderLock()

 Next

End Sub

The only differences in this code from the UpgradeToWriterLock are the lines:

objCookie = objLock.UpgradeToWriterLock(Timeout. Infinite)

And

objLock.DowngradeFromWriterLock(oCookie)

Console.WriteLine("Downgraded lock")

Instead of just waiting until the ReleaseReaderLock is called, we explicitly

change the writer lock to a reader lock. The only real difference between downgrading

and releasing the lock are with any other waiting writer locks. If you downgrade and still

Page 35: Multi Threading in VB

8/14/2019 Multi Threading in VB

http://slidepdf.com/reader/full/multi-threading-in-vb 35/46

have waiting writer locks, they must continue to wait until the downgraded lock is

released. You should see output similar to the following:

1 Thread 2

lData is now 2

Downgraded lock 

2 Thread 2

lData is now 3

Downgraded lock 

3 Thread 2

lData is now 4

Downgraded lock 

4 Thread 2

lData is now 5

Downgraded lock 

5 Thread 2

lData is now 6

Downgraded lock 

6 Thread 2

6 Thread 2

lData is now 7

Downgraded lock 

7 Thread 2

lData is now 8

Downgraded lock 

Page 36: Multi Threading in VB

8/14/2019 Multi Threading in VB

http://slidepdf.com/reader/full/multi-threading-in-vb 36/46

8 Thread 2

lData is now 9

Downgraded lock 

9 Thread 2

lData is now 10

Downgraded lock 

lData is now 11

Downgraded lock 

Two other methods of note on the ReaderWriterLock class are ReleaseLock 

and RestoreLock. ReleaseLock immediately drops all locks that the current thread

holds. It returns a LockCookie just like UpgradeToWriterLock that can be used in

RestoreLock. When used, the LockCookie returns the thread back to the exact lock state

that it held before. To handle the fact that other threads could have acquired locks on the

object, the method will block until it can resolve all of its previous locks. The code is as

follows:

Dim oLock As ReaderWriterLock 

Private Sub btnRun_Click(ByVal sender As System.Object, ByVal

e As System. EventArgs) Handles btnRun.Click 

Dim Thread1 As Thread

Dim objCookie As LockCookie

objLock = New ReaderWriterLock()

Thread1 = New Thread(AddressOf Thread1Work)

objLock.AcquireWriterLock(Timeout. Infinite)

Thread1.Start()

Thread.Sleep(1000)

objCookie = objLock.ReleaseLock 

Page 37: Multi Threading in VB

8/14/2019 Multi Threading in VB

http://slidepdf.com/reader/full/multi-threading-in-vb 37/46

Thread1 = New Thread(AddressOf Thread1Work)

Thread1.Start()

Thread.Sleep(1000)

objLock.RestoreLock(oCookie)

Thread.Sleep(1000)

Thread1 = New Thread(AddressOf Thread1Work)

Thread1.Start()

End Sub

Private Sub Thread1Work()

Try

objLock.AcquireReaderLock(10)

Console.WriteLine("Got a reader lock")

objLock.ReleaseReaderLock()

Catch

Console.WriteLine("Reader lock not held")

End Try

End Sub

Examining the code, we first see that a writer lock is acquired. Thread1 is then

started to show that it can’t acquire a reader lock on the object. The main thread then

releases the writer lock by calling ReleaseLock and saving its state to objCookie.Thread1

is then restarted acquiring the reader lock. A call to RestoreLock is called then with the

LockCookie passed to it. When thread one is restarted at that point it cannot acquire its

reader lock. The call to RestoreLock has replaced the writer lock on the object. The

output looks like the following:

Reader lock not held

Got a reader lock 

Page 38: Multi Threading in VB

8/14/2019 Multi Threading in VB

http://slidepdf.com/reader/full/multi-threading-in-vb 38/46

Reader lock not held

Another interesting pair of functions in the ReaderWriterLock class is the

function WriterSeqNum and AnyWritersSince.WriterSeqNum returns the sequence

number of the current lock in the internal queue of the ReaderWriterLock class. Thisqueue keeps the order of the threads that have requested reader or writer locks on an

object. AnyWritersSince will tell if any writer locks have been released since the call to

WriterSeqNum. This is a good method to check if a piece of data has been updated on

another thread. AnyWritersSince could be used in a large, time-consuming report

situation. If no writers have updated the report data then there is no need to recalculate

the report. The following code will show the methods in action.

Dim objLock As ReaderWriterLock 

Private Sub btnRun_Click(ByVal sender As System.Object, ByVal

e As System. EventArgs) Handles btnRun.Click 

Dim objCookie As LockCookie

Dim SeqNum As Integer 

Dim Thread1 As Thread

objLock = New ReaderWriterLock()

Thread1 = New Thread(AddressOf Thread1Work)

objLock.AcquireWriterLock(Timeout. Infinite)

SeqNum = objLock.WriterSeqNum

If objLock.AnyWritersSince(SeqNum) = False Then

Console.WriteLine("We see that no writers have

released yet")

End If 

objLock.ReleaseWriterLock()

Thread1.Start()

Page 39: Multi Threading in VB

8/14/2019 Multi Threading in VB

http://slidepdf.com/reader/full/multi-threading-in-vb 39/46

Thread1.Join()

If objLock.AnyWritersSince(SeqNum) = True Then

Console.WriteLine("We see that a writer has released

now")

End If 

End Sub

Public Sub Thread1Work()

objLock.AcquireWriterLock(Timeout. Infinite)

objLock.ReleaseWriterLock()

End Sub

First a writer lock is acquired on objLock. The sequence number is saved in

SeqNum. Then a test to AnyWritersSince is made. Since no other threads have acquired

any writer locks and released them, the method returns false. Next a thread, Thread1, is

started and waited on. This thread simply acquires a writer lock and releases it. The

main thread then checks AnyWritersSince again using the saved off sequence number.

Since another thread has released a writer lock the method return true this time. The

following output is returned.

We see that no writers have released yet

We see that a writer has released now

COM+ Synchronization

The dot net framework provides many enterprise services that can be used to

 build enterprise applications, one of which is the COM+ method of synchronization.

COM+ offers developers many helpful techniques such as transaction handling between

objects, loosely coupled events, object pooling and synchronization, which we will

discuss here, to name a few. This synchronization method allows the usage of a concept

called a context to provide ways to lock code for synchronization. This method can be

Page 40: Multi Threading in VB

8/14/2019 Multi Threading in VB

http://slidepdf.com/reader/full/multi-threading-in-vb 40/46

implemented on any class that is derived from ContextBoundObject, or from any class

that derives from ContextBoundObject.

When deriving a class from ContextBoundObject, the attribute

<SYNCHRONIZATION()>can be used. This tells the runtime to providesynchronization for the entire class by making each class instance only accessible by one

thread at a time. This case study will give a brief overview of this topic, as it is out of the

scope of the article. Entire books have been written on the subject of COM+. For further 

reading on COM+ get a copy of Professional Visual Basic Interoperability – COM and

VB6 to .NET, ISBN 1-861005-65-2.

When you use the attribute, COM+ will create a proxy for you that will run all

instances of your object in its context. COM+ will marshal all calls across this proxy

where a performance penalty occurs. The service guarantees that only one thread is

available to run each object at a time.

Earlier the timed methods of the WaitHandle classes were discussed. Recall

that the second parameter of the method was a boolean method that determined whether 

to release the synchronized context along with the object lock. If your classes use COM+

synchronization True should be passed for this parameter or deadlocks are risked. True

tells COM+ to exit its synchronized context before the runtime allows the thread to wait.

This allows other threads to then get access to the context avoiding deadlocks. If you

don’t exit the context, the . Net runtime will allow other threads access to the locked

object since an exit method has been called. When the next thread acquires a lock on the

locking object it will then try to enter the context, which is still locked resulting in a

deadlock.

While COM+ synchronization provides another easy way to provide

synchronization, be careful when using it. Many calls to a COM+ synchronized object

will degrade your application greatly because of all the marshaling across the proxy. Be

sure to test responsiveness when using it.

Page 41: Multi Threading in VB

8/14/2019 Multi Threading in VB

http://slidepdf.com/reader/full/multi-threading-in-vb 41/46

Apartments and Window’s Form Synchronization

Now that we have examined all the methods that Visual Basic offers for 

synchronization, we will take a look at Window’s Form projects and what apartment

threading is. The most common types of threading on the Windows platform are singlethreaded apartments (STA) or multithreaded apartments (MTA). Window’s forms must

 be hosted in an STA apartment because some Window’s Form controls are based on

standard Windows COM controls that require an STA environment. Background threads

can still be utilized to update forms, but synchronization must be done differently. As we

examine the two apartment styles, we will look at how to do correct synchronization with

Window’s Forms.

By default all Windows’ Form projects in Visual Basic are STA. Visual Basic

applies the <STATHREAD()>attribute to the main entry point in the application for you

 behind the scenes. While you could override this attribute and change it to an MTA

apartment, you should not or problems will occur with the COM controls as discussed

above.

So what is an STA apartment?The apartment concept comes from the early

COM days. Basically, STA means that only one thread can access an object, the thread

that created it. Any future access to the object must also be done on the original thread.

This is the one key reason why you should never update a control on a Window’s Form

from another thread. Most COM objects require STA.

MTA, sometimes called free threading, is much harder to program than STA.

This is another reason why we encounter STA COM components most of the time. MTA

means that more than one thread can access an object at any given point in time safely.

When programming for MTA, you must be sure to include good synchronization and

design as discussed in the case study. Any number of threads could be accessing objects

in your library at any time.

The type of threading model that the current thread is using can be determined

simply with the following code.

Dim sThreadType As String

Page 42: Multi Threading in VB

8/14/2019 Multi Threading in VB

http://slidepdf.com/reader/full/multi-threading-in-vb 42/46

sThreadType = Thread.CurrentThread.ApartmentState.ToString()

MessageBox.Show(sThreadType)

sThreadType will equal “STA” or “MTA” after the call. There is also an

ApartmentState object that can be set to Thread.CurrentThread.AppartmentState().

Dim Apt as ApartmentState

Apt = Thread.CurrentThread.ApartmentState()

MessageBox.Show(apt.ToString())

Window’s Form classes provide built in methods to update GUI elements from

other threads. These methods should be used exclusively. The methods are called

Invoke, BeginInvoke, EndInvoke and CreateGraphics. All of the methods can be calledfrom any thread. When called, the methods provide a way to work with the control from

the main Window’s Form thread. Let’s see how we can use the methods.

The Invoke method takes a delegate for a parameter. A delegate is basically a

variable that points to a method. The variable in this case tells the Invoke method what

function to run. This delegate is run under the control’s owner thread and not the calling

thread, preserving the STA style. Let’s take a look at a simple example that adds entries

to a textbox control using a separate thread. A button and a multi-line textbox are addedto a Window’s Form.

Private Sub btnStart_Click(ByVal sender As System.Object, ByVal

e As System.EventArgs) Handles btnStart.Click 

Dim Thread1 As Thread

Thread1 = New Thread(AddressOf Thread1Work)

Thread1.Start()

End Sub

Private Delegate Sub DelAddItem()

Private Sub Thread1Work()

Dim del As DelAddItem

Page 43: Multi Threading in VB

8/14/2019 Multi Threading in VB

http://slidepdf.com/reader/full/multi-threading-in-vb 43/46

del = New DelAddItem(AddressOf DelegateWork)

txtList.Invoke(del)

Console.WriteLine("Thread 1 Done")

End Sub

Private Sub DelegateWork()

Dim i As Integer 

For i = 0 To 100

txtList.Text = txtList.Text + "A New Line: " &

i.ToString() + vbCrLf 

 Next 'i

Console.WriteLine("Delegate Done")

End Sub

To call Invoke, a delegate sub is created. This sub simply adds a new line to

the textbox with the words “A New Line”. When our new thread is started a new

instance of the delegate is created. The new delegate is then passed to txtList. Invoke

updating the text.

The Invoke method runs any code in the delegate synchronously on the thread.

The output from the run will show this:

Delegate Done

Thread 1 Done

Thread 1 will not continue running until the delegate is finished.

Sometimes asynchronous calls are preferred. The BeginInvoke and EndInvoke

allow updating the GUI using built in asynchronous technology in the framework. The

two methods take the same delegate that Invoke did. They only call the code

asynchronously. EndInvoke will return the resulting value from an asynchronous

BeginInvoke call. If the BeginInvoke is still running, EndInvoke will block until the

Page 44: Multi Threading in VB

8/14/2019 Multi Threading in VB

http://slidepdf.com/reader/full/multi-threading-in-vb 44/46

BeginInvoke call finishes. It will not terminate the BeginInvoke call. An example is

 below.

Private Sub btnStart_Click(ByVal sender As System.Object,

ByVal e As System.EventArgs) Handles btnStart.Click 

Dim Thread1 As Thread

Thread1 = New Thread(AddressOf Thread1Work)

Thread1.Start()

End Sub

Private Delegate Sub DelAddItem()

Private Sub Thread1Work()

Dim del As DelAddItem

Dim Result As IAsyncResult

del = New DelAddItem(AddressOf DelegateWork)

Result = txtList.BeginInvoke(del)

Console.WriteLine("Thread 1 Done")

Console.WriteLine(Result.IsCompleted.ToString())

txtList.EndInvoke(Result)

Console.WriteLine(Result.IsCompleted.ToString())

End Sub

Private Sub DelegateWork()

Dim i As Integer 

For i = 0 To 100

txtList.Text = txtList.Text + "A New Line: " &

i.ToString() + vbCrLf 

Page 45: Multi Threading in VB

8/14/2019 Multi Threading in VB

http://slidepdf.com/reader/full/multi-threading-in-vb 45/46

 Next 'i

Console.WriteLine("Delegate Done")

End Sub

Output:

Thread 1 Done

False

Delegate Done

True

As we see from the output, Thread 1 completed before the delegate finished.

Then the first call to Result.IsCompleted returns false, signifying that the delegate is still

running. Thread 1 is then put to sleep with the EndInvoke call allowing the delegate time

to finish. The next call to Result.IsCompleted returns true.

The code also shows two methods of getting the status of an asynchronous

call. The first method was the line Result = txtList.BeginInvoke(del). The Result

variable will contain the current results of the asynchronous call. The other method is

with the EndInvoke call, which we said earlier, would block until the asynchronous call is

finished. The last output of true shows that this behavior happened.

When using graphics drawing methods with Window’s Forms you must be sure

to do all work on the main thread also. The CreateGraphics method makes sure of this

for you. It can be called from other threads safely like the invoke methods. The

Graphics object returned will run all calls in the correct thread for you. The Graphics

object is considered thread safe so no additional locking objects are necessary.

A Quick Word on the volatile Keyword

In your reading or study of .Net code, the volatile C# keyword might come up.

This keyword does not exist in Visual Basic. Don’t worry though; it doesn’t add any

functionality to C# that can’t be done with the other synchronization objects discussed in

this case study.

Page 46: Multi Threading in VB

8/14/2019 Multi Threading in VB

http://slidepdf.com/reader/full/multi-threading-in-vb 46/46

The volatile keyword tells the compiler that the variable it references could

change at anytime and that no optimizations should be done to it. It will prohibit the

compiler from storing the variable in a register and force it read it new from memory

each time.

Variables marked as volatile aren’t necessarily thread safe. They only insure

that each read of the variable is the latest information. To see what a declaration looks

like look at the following code snip-it, which declares an Integer variable as volatile.

 private volatile int MyInteger;

Use of Monitor is a much safer and better way to handle synchronization. It

guarantees that the variable is up to date as only one thread is accessing the variable at a

time. It is safe to replace volatile variable access with Monitor blocks of code or any

other synchronization method discussed in the case study that fit your needs. Good

synchronization practice will eliminate the need for volatile.

Summary

Multithreaded applications are a must today.The Dot Net Framework makes

creating these applications much easier than traditional programming methods. Be sure

to take advantage of multithreading and of all available methods of synchronization.

When designing for multithreaded applications remember the age-old proverb:

An ounce of prevention is worth a pound of cure. It is much easier to prevent deadlocks

and other multithreaded bugs by taking a few extra minutes and trying to prevent them.

You will usually spend a lot of time trying to find the cause of these bugs when reported

from the field, as they don’t usually show up stepping through code, but only when

running at full speed.