Making Your Code Thread-Safe

Making Your Code Thread-Safe

Threading issues may affect Visual Basic 6 ActiveX DLLs or UserControls that you upgrade to Visual Basic .NET, especially since the Visual Basic Upgrade Wizard does not make your code thread-safe when upgrading it. You will need to make manual changes to your code as explained later in this section to make it thread-safe.

Such threading issues are uncommon, since components created in Visual Basic 6 are thread-safe by nature. Thread safety is accomplished in Visual Basic 6 by letting only a single thread access a component. Even if multiple threads are attempting to gain access to the component, all requests are synchronized through the single accessor thread associated with the component.

Visual Basic .NET components (including UserControls), on the other hand, are not thread-safe. Multiple threads are allowed to access the component simultaneously. There is no synchronization of threads on behalf of a Visual Basic .NET component. As a result, code that is thread-safe in Visual Basic 6 becomes unsafe after being upgraded to Visual Basic .NET. This change will affect you if you are running your component in a multithreaded environment such as Internet Explorer, Internet Information Services, or COM+. If you are not sure whether your component will be run in a multithreaded environment, it is always best to err on the side of caution and ensure that your component is thread-safe.

When more than one thread is executing your code, each thread has access to the same shared data: member variables and global variables. Allowing more than one thread to manipulate shared data at the same time can lead to problems. For example, if two threads are attempting to increment a count of items in a collection simultaneously, the count may be end up being incremented by 1 and not 2. This will happen if both threads obtain the current count at about the same time and add 1. Neither thread will see what the other thread is doing. We call this a synchronization problem, since it involves a situation in which only one thread at a time should be allowed to perform the operation.

For example, a synchronization problem can occur when one thread caches a global or member variable value in a local variable and performs a calculation on that variable. Let s take a look at the following Visual Basic .NET code.

Public Class GreedyBank    Private m_Balance As Decimal    Public Sub New()       m_Balance = 20    End Sub    Public Function Withdraw(ByVal Amount As Decimal) As String       'Protect against negative balance       If m_Balance >= Amount Then          m_Balance = m_Balance - Amount       Else          Return "The amount you requested will overdraw " & _             "your account by $" & Amount   m_Balance       End If    End Function End Class

This example is pretty straightforward. A client application calls Withdraw. Withdraw takes the current customer balance and deducts the requested withdrawal amount. To ensure that the customer s account does not become overdrawn by the transaction, the function checks to make sure that the customer has enough money to cover the withdrawal amount.

This works great when only one thread at a time is executing the function. However, you can run into problems if two threads are executing the function simultaneously.

Let s suppose that the first thread has executed up to the following line:

m_Balance = m_Balance - Amount

The customer s balance checked out, so the thread is ready to deduct the Amount. Assume that the customer has just enough money ($20) to cover the withdrawal request of $20. Just before the first thread executes the above statement, a second thread comes along, requesting an amount of $20 and completing the following statement:

        If m_Balance >= Amount Then

The second thread also sees that the bank balance, $20, is sufficient to cover the withdrawal request and proceeds to the next line:

m_Balance = m_Balance - Amount

At this point, the customer is rewarded with an account overdraft and the fees that accompany it. The first thread subtracts the withdrawal, leaving a balance of $0. The second thread, having successfully bypassed the sufficient-balance-to-cover-withdraw-amount check, then executes the same line, leaving a net balance of $20. Here code that on the surface appears to be perfectly legitimate leads to unanticipated results although the customer would need to submit two withdrawal requests simultaneously to encounter the problem.

Using a SyncLock Block

To fix this synchronization problem, you need to make sure that only one thread at a time has access to your shared member or global variables. In other words, you need to synchronize access to your data, letting only one thread at time execute certain blocks of code that manipulate shared data. You can do this by using the new Visual Basic .NET keyword SyncLock.

A SyncLock block ensures that only one thread at a time is executing code within the block. In the earlier code example, we can fix our negative balance problem by placing a SyncLock around the contents of the Withdraw function:

SyncLock GetType(GreedyBank)    'Protect against negative balance    If m_Balance >= Amount Then       m_Balance = m_Balance - Amount    Else       Return "The amount you requested will overdraw " & _          "your account by $" & Amount   m_Balance    End If End SyncLock

If there is any chance that your Visual Basic .NET component or UserControl will run in a multithreaded environment, we highly recommend using Sync Lock blocks to protect your shared data. Taking the extra time to do this work up front can save you from grief down the road.



Upgrading Microsoft Visual Basic 6.0to Microsoft Visual Basic  .NET
Upgrading Microsoft Visual Basic 6.0 to Microsoft Visual Basic .NET w/accompanying CD-ROM
ISBN: 073561587X
EAN: 2147483647
Year: 2001
Pages: 179

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net