Multithreading in VB.NET

COM+ and Windows Forms Synchronization

COM+ Synchronization

The .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 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 <Syncronization()>can be used. This tells the runtime to provide synchronization 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.

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 single threaded 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
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 called from 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 added to 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
    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 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 T hread1 As Thread
    Thread1 = New Thread(AddressOf Thread1Work)
    Thread(1.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)
    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.

You might also like...

Comments

About the author

John Spano United States

John Spano cofounder and CTO of NeoTekSystems, a Greenville, South Carolina technology consulting company. NeoTekSystems offers IT consulting, custom programming, web design and web hosting. We ...

Interested in writing for us? Find out more.

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“To iterate is human, to recurse divine” - L. Peter Deutsch