How to synchronize the access to a shared resource in a multithreading environment with Visual Basic .NET or Visual Basic 2005 (316136)



The information in this article applies to:

  • Microsoft Visual Basic 2005
  • Microsoft Visual Basic .NET (2003)
  • Microsoft Visual Basic .NET (2002)

This article was previously published under Q316136

SUMMARY

Visual Basic .NET or Visual Basic 2005 applications can perform multiple tasks simultaneously by using multithreading. Multithreading can start different threads to complete different tasks simultaneously, and multithreading improves the performance and responsiveness of your applications.

Because multiple threads can access a resource at the same time, you may want to synchronize individual threads with other parts of your program. This article describes some common scenarios with multithreading programming and explains how to synchronize the access to a shared resource among the multiple threads.

back to the top

How to Protect Your Global Data in Modules in a Multithreaded Environment

The public fields in modules are accessible to all the threads in your application. To synchronize the access to the public fields, you can use property instead of field, and use a ReaderWriterLock object to control the access. To do this, follow these steps:
  1. Open Visual Studio .NET or Visual Studio 2005.
  2. On the File menu, click New Project.
  3. In the tree view on the left, select Visual Basic, and then select Console Application in the list view on the right. Press ENTER.
  4. Module1 is generated automatically. Delete all the code in Module1, and then paste the following code into Module1:
    Option Explicit On 
    Option Strict On
    
    Imports System
    Imports System.Threading
    
    Module Module1
    
        Sub Main()
            Dim threadArray(20) As Thread
            Dim threadNum As Integer
    
            'Create 20 threads.
            For threadNum = 0 To 19
                threadArray(threadNum) = New Thread(AddressOf AccessGlobalResource)
            Next threadNum
    
            'Start the threads.
            For threadNum = 0 To 19
                threadArray(threadNum).Start()
            Next
    
            'Wait until all of the thread spawn out finish.
            For threadNum = 0 To 19
                threadArray(threadNum).Join()
            Next
    
            Console.WriteLine("All operations have completed. Press enter to exit")
            Console.ReadLine()
        End Sub
    
        Sub AccessGlobalResource()
            Dim rnd As New Random()
            Dim theNumber As Long
    
            If rnd.Next Mod 2 <> 0 Then
                theNumber = Number
            Else
                theNumber = rnd.Next
                Number = theNumber
            End If
        End Sub 'AccessGlobalResource
    End Module
    
    					
  5. Right-click the project in Solution Explorer, and then click Add/Add Module. Add Module2.vb to the project.

    Note In Visual Studio 2005, right-click the project in Solution Explorer, and then click Add/Module.
  6. Delete all the code that is generated by default, and then paste the following code into Module2:
    Option Explicit On 
    Option Strict On
    
    Imports System
    Imports System.Threading
    
    Module Module2
        Private rwl As New ReaderWriterLock()
        Private myNumber As Long
    
        Public Property Number() As Long
            Get
                'Acquire a Read lock on the resource.
                rwl.AcquireReaderLock(Timeout.Infinite)
                Try
                    Console.WriteLine("Thread:{0} starts getting the Number", Thread.CurrentThread.GetHashCode)
                    Thread.Sleep(50)
                    Number = myNumber
                    Console.WriteLine("Thread:{0} got the Number", Thread.CurrentThread.GetHashCode)
                Finally
                    'Release the lock.
                    rwl.ReleaseReaderLock()
                End Try
            End Get
    
            Set(ByVal Value As Long)
                'Acquire a Write lock on the resource.
                rwl.AcquireWriterLock(Timeout.Infinite)
                Try
                    Console.WriteLine("Thread: {0} start writing the Number", Thread.CurrentThread.GetHashCode)
                    Thread.Sleep(50)
                    myNumber = Value
                    Console.WriteLine("Thread: {0} written the Number", Thread.CurrentThread.GetHashCode)
                Finally
                    'Release the lock.
                    rwl.ReleaseWriterLock()
                End Try
            End Set
        End Property
    End Module
    
    						
  7. Compile the project and then run it.
back to the top

How to Make Your Class Thread-Safe

Multiple threads may try to access an object at the same time. When more than one thread simultaneously competes for access to an object, it is possible that some threads may get an invalid state if another thread modifies the resource at the same time. For example, if a thread is reading the object's field while another thread is modifying the field, the first thread may get an invalid state of the field. This situation is called a race condition.

To avoid this situation, you can protect critical sections of your code from race conditions by employing locks. A lock, represented by the Visual Basic keyword SyncLock Statement, allows a single thread of execution to obtain exclusive execution rights on an object. The following example steps demonstrate locks:
  1. Open Visual Studio .NET.
  2. On the File menu, click New Project.
  3. In the tree view on the left, select Visual Basic, and then select Console Application in the list view on the right. Press ENTER.
  4. Module1 is generated automatically. Delete all the code in Module1, and then paste the following code into Module1:
    Option Explicit On 
    Option Strict On
    
    Imports System
    Imports System.Threading
    
    Module Module1
        Public WorkItemNum As Integer = 20
        Public Done As New AutoResetEvent(False)
    
        Sub Main()
            Dim threadNum As Integer
            Dim AStudent As New Student()
    
            'Queue up 20 work items in the ThreadPool.
            For threadNum = 0 To WorkItemNum - 1
                ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf AccessClassResource), AStudent)
            Next threadNum
    
            Done.WaitOne()
    
            Console.WriteLine("All operations have completed. Press enter to exit")
            Console.ReadLine()
        End Sub
    
        Sub AccessClassResource(ByVal state As Object)
            Dim rnd As New Random()
            Dim theName As String
            Dim AStudent As Student = CType(state, Student)
    
            If rnd.Next Mod 2 <> 0 Then             'Do some thing with the static member: TeacherName.
                If rnd.Next Mod 2 <> 0 Then  'write to the TeacherName.
                    Select Case rnd.Next Mod 3
                        Case 0
                            Student.TeacherName = "Tom"
                        Case 1
                            Student.TeacherName = "Mike"
                        Case 2
                            Student.TeacherName = "John"
                    End Select
                Else 'read the static member.
                    theName = Student.TeacherName
                End If
            Else   'Do something with the instance member.
                If rnd.Next Mod 2 <> 0 Then  'write to the instance member.
                    Select Case rnd.Next Mod 3
                        Case 0
                            AStudent.SetName("Janet")
                        Case 1
                            AStudent.SetName("David")
                        Case 2
                            AStudent.SetName("Ben")
                    End Select
                Else 'read the instance member.
                    theName = AStudent.GetName()
                End If
            End If
    
            'Because it is possible that multiple threads may access the WorkItemNum, 
            'you must use the Interlocked.Decrement to decrease it.
            If Interlocked.Decrement(WorkItemNum) = 0 Then
                'Set the event to notify the main thread that all work is completed.
                Done.Set()
            End If
        End Sub 'AccessClassResource
    End Module
    
    					
  5. Right-click the project in Solution Explorer, and then click Add/Add Class. Add Class1.vb to the project.

    Note In Visual Studio 2005, right-click the project in Solution Explorer, and then click Add/Class.
  6. Delete all the code that is generated by default, and then paste the following code into Class1.vb:
    Option Explicit On 
    Option Strict On
    
    Imports System
    Imports System.Threading
    
    Public Class Student
        Private Shared myTeacherName As String = "Bill"
        Private myName As String = "Grace"
    
        Public Shared Property TeacherName() As String
            Get
                Dim theName As String
    
                SyncLock GetType(Student) 'Synchronize access to the shared member.
                    Console.WriteLine("Thread {0} starts to get the teacher's name", Thread.CurrentThread.GetHashCode)
                    theName = myTeacherName
                    'Wait for 0.3 second.
                    Thread.Sleep(300)
                    Console.WriteLine("Thread {0} finished to get the teacher's name:{1}.", Thread.CurrentThread.GetHashCode, theName)
                End SyncLock
    
                Return theName
            End Get
            Set(ByVal Value As String)
                SyncLock GetType(Student) 'Synchronize access to the shared member.
                    Console.WriteLine("Thread {0} starts to set the teacher's name.", Thread.CurrentThread.GetHashCode)
                    myTeacherName = Value
                    'Wait for 0.3 second.
                    Thread.Sleep(300)
                    Console.WriteLine("Thread {0} finished to set the teacher's name:{1}.", Thread.CurrentThread.GetHashCode, Value)
                End SyncLock
            End Set
        End Property
    
        Public Function GetName() As String
            Dim theName As String
    
            SyncLock Me 'Synchronize access to the shared member.
                Console.WriteLine("Thread {0} starts to get the student's name.", Thread.CurrentThread.GetHashCode)
                theName = myName
                'Wait for 0.3 second.
                Thread.Sleep(300)
                Console.WriteLine("Thread {0} finished to get the student's name:{1}", Thread.CurrentThread.GetHashCode, theName)
    
                Return theName
            End SyncLock
        End Function
    
        Public Function SetName(ByVal NewName As String) As String
            Dim theOldName As String
    
            SyncLock Me 'Synchronize access to the shared member.
                Console.WriteLine("Thread {0} starts to set the student's name.", Thread.CurrentThread.GetHashCode)
                theOldName = myName
                myName = NewName
                'Wait for 0.3 second.
                Thread.Sleep(300)
                Console.WriteLine("Thread {0} finished to set the student's name:{1}", Thread.CurrentThread.GetHashCode, NewName)
            End SyncLock
    
            Return theOldName
        End Function
    End Class
    
    					
  7. Compile the project and then run it.
back to the top

REFERENCES

For more information about this subject, see the following Microsoft MSDN Web sites: back to the top

Modification Type:MinorLast Reviewed:10/3/2006
Keywords:kbvs2005swept kbvs2005applies kbHOWTOmaster KB316136 kbAudDeveloper