Thread。Sleep(1500) elements。Add(30) End Sub Sub Main() elements。Add(10) elements。Add(20) Dim thread1 As New Thread(AddressOf Task1) Dim thread2 As New Thread(AddressOf Task2) thread1。Start() thread2。Start() End Sub End Module The change is the bolded code; which instantiates the type System。Collections。 ReadOnlyCollection; to which we pass the elements list。 The ReadOnlyCollection provides the base class for a generic read…only collection。 The For Each iterator then iterates a collection that is read…only; but based on the original collection。 However; running the code will result in the same exception。 This demonstrates that ReadOnlyCollection does not take a snapshot; but masks the collection。 The mask disables the addition of items to the collection; but because the other thread is taking a shortcut and editing the original collection; the read…only collection is modified as well。 Let’s say that converting the collection into a read…only collection had worked。 It would not have solved anything。 A read…only collection means that the second thread would generate an exception because you can’t add elements to a collection that is read…only。 The point is that when writing multithreaded code that shares variables; you don’t have an easy solution; because you are trying to solve the problem of how to keep multiple cooks productive in a single kitchen。 We are trying to solve a classic reader/writer problem; where some threads are interested only in reading the data; and other threads are interested only in modifying the data。 One way to synchronize the readers and writers is to use an exclusive lock; so that only one thread may read or write。 …………………………………………………………Page 374…………………………………………………………… 352 CH AP T E R 1 3 ■ L E A R N I N G A B OU T M U L T I TH R E A DI N G Using Exclusive Locks When using exclusive locks in ; you are saying; “Only one thread may execute this piece of code。” If two threads want to execute a particular piece of code; one will be granted access; while the other thread waits until the granted thread has exited the code block。 It is important to understand that an exclusive lock grants access to code; not data; but that code could access data。 And because only one thread is accessing the code; it is implied that only one thread can access the data。 The following is an example of code that uses exclusive locks。 。 。 。 Module ThreadProblem Dim elements As List(Of Integer) = New List(Of Integer)() Sub Task1() Thread。Sleep(1000) SyncLock elements Dim item As Integer For Each item In elements Console。WriteLine(〃Item (〃 & item & 〃)〃) Thread。Sleep(1000) Next End SyncLock End Sub Sub Task2() Thread。Sleep(1500) SyncLock elements elements。Add(30) End SyncLock End Sub Sub Main() elements。Add(10) elements。Add(20) Dim thread1 As New Thread(AddressOf Task1) Dim thread2 As New Thread(AddressOf Task2) thread1。Start() thread2。Start() End Sub End Module The bolded lines use the SyncLock and End SyncLock keywords; which represent a code block of exclusive access。 The thread is granted access to only a single code block in each instance。 Looking at the code within the block; you can see that the collection is accessed in two locations。 Using the exclusive SyncLock argument where a single thread can access only a single code block; one thread will write to the collection; and another thread will read from the collection。 …………………………………………………………Page 375…………………………………………………………… C HA P TE R 1 3 ■ L E AR N IN G AB O U T M U L T IT HR E AD IN G 353 The SyncLock statement has a parameter that is a reference to lock against。 In both threads; the reference is elements。 The mon reference synchronizes access to code。 At any given point in time; the code contained within the SyncLock block will have only a single thread executing。 This implements the desired feature; where only one thread is accessing code that reads or writes to the collection。 The flow of the program is as follows: 1。 Both threads wait。 2。 After a 1 second; thread 1 acquires a lock because no other thread has done so。 3。 Thread 1 executes its code。 4。 Once thread 1 has started executing the synchronized code; no other code can acquire the lock that is associated with the variable elements。 5。 When thread 2 wakes up after a sleep of 1。5 seconds; it will attempt to acquire the lock; but it can’t because thread 1 is still holding the lock。 So the second thread must wait。 6。 Thread 1 eventually exits the synchronized code block; allowing the second thread to add an element to the collection。 This time; no exception is thrown。 The reference to lock against does not need to be the reference that is manipulated within the code block。 The reference is just that: an arbitrary reference。 You could use a different object instance; and even instantiate an object like this: Dim _syncRoot As Object = New Object() 。 。 。 SyncLock _syncRoot 。