SyncLock elements Do While (elements。Count 《 3) Thread。Sleep(1000) Loop items = elements。ToArray() End SyncLock Dim item As Integer For Each item In items Console。WriteLine(〃Item (〃 & item & 〃)〃) Thread。Sleep(1000) Next 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 iteration code waits until the collection count is 3。 However; this never happens; because the thread that could make the collection count 3 is waiting for the lock to bee free。 The bolded code is the waiting code that queries whether the collection count is 3。 If it is not; the thread waits for 1 second and asks again。 But throughout all of this waiting; the lock is never given up; and thus the second thread that could add an element is waiting。 The code will deadlock。 Without modifying the locks; here is a tweak that will avoid the deadlock (the thread sleeps for only 0。5 seconds instead of 1。5 seconds; so the writing thread can get in first before the reader thread gets in): Sub Task2() Thread。Sleep(500) SyncLock elements elements。Add(30) End SyncLock End Sub …………………………………………………………Page 379…………………………………………………………… 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 357 The single change (shown in bold) makes the code work。 In the first version; the timing of the code was such that the reading thread went first。 In this version; the writing thread goes first。 This shows that deadlocks are often timing…related。 The annoying part of deadlocks is that your code’s behavior is not deterministic。 Determin istic behavior is when an action will result in a single result; as in the case with most source code。 Typically; when you have a bug; you didn’t think far enough ahead and can work through the error systematically。 However; with threading; your code ceases to be deterministic; because timing can change the behavior。 Timing can be an influence in many ways: resource swapping; debuggers; microprocessor speed; and a host of other features。 To make the code deterministic; you need to fix the part of the code that hung onto the lock when it should not have。 Remember the cardinal rule: keep a lock for as short a time as possible。 You need to use a more advanced lock construct that allows you to wait for data to bee available。 has quite a few constructs related to threading and synchronization; and each construct is specific to the type of problem you are trying to solve。 In the case of a deadlock; you want to use the Monitor type。 The Monitor type is an advanced synchronization type that allows locking and pulsing of trigger signals for those threads that are waiting。 Let’s return to our multiple cooks in the kitchen analogy。 Say one cook needs a particular fish pan; which is already being used by another cook。 Does the waiting cook tap her foot beside the cook doing the cooking? Or does the waiting cook do something else and ask the cook using the pan to inform her when the pan is free? Most likely; the cook will do something else and wait to be informed that the pan is free。 This concept of working together and being able to notify other lock users is a powerful feature programmed into the Monitor type。 Monitor has the ability to take a lock; give it up so others can get the lock; and then take it back again。 When using the Monitor type; you do not declare a block of code that is protected; because Monitor is much more flexible than that。 For example; you could define a class that has an instance…level lock mechanism; like this: Class DoSomething Public Sub GetLock() Monitor。Enter(Me) End Sub Public Sub ReleaseLock() Monitor。Exit(Me) End Sub End Class Any code that uses a Monitor is not restricted to where it’s placed in the code; but a Monitor is bound to a thread。 So if a thread grabs a Monitor (by calling the Enter() method on the Monitor object); it has control until the thread dies or the thread gives up control (by calling the Exit() method on the Monitor object)。 This has the added benefit that; once having acquired a lock; a Monitor can get it over and over again。 However; if the same thread locked the Monitor five times; the same thread needs to release it five times before another thread can be granted access to the lock。 The following is the rewritten two…thread source code that uses a Monitor。 …………………………………………………………Page 380…………………………………………………………… 358 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 Sub Task1() Dim items As Integer() Thread。Sleep(1000) Monitor。Enter(elements) Do While (elements。Count 《 3) Monitor。Wait(elements; 1000) Loop