<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://blogs.msdn.com/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Vance Morrison's Weblog : Concurrency </title><link>http://blogs.msdn.com/vancem/archive/tags/Concurrency+/default.aspx</link><description>Tags: Concurrency </description><dc:language>en-US</dc:language><generator>CommunityServer 2.1 SP1 (Build: 61025.2)</generator><item><title>Analysis of Reader-Writer lock </title><link>http://blogs.msdn.com/vancem/archive/2006/03/29/564854.aspx</link><pubDate>Thu, 30 Mar 2006 04:11:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:564854</guid><dc:creator>vancem</dc:creator><slash:comments>14</slash:comments><comments>http://blogs.msdn.com/vancem/comments/564854.aspx</comments><wfw:commentRss>http://blogs.msdn.com/vancem/commentrss.aspx?PostID=564854</wfw:commentRss><description>&lt;P&gt;In my &lt;A href="/vancem/archive/2006/03/28/563180.aspx"&gt;last post &lt;/A&gt;I posted &lt;A href="/vancem/attachment/563180.ashx"&gt;&lt;SPAN style="FONT-SIZE: 10pt; COLOR: #0033cc"&gt;&lt;FONT size=3&gt;readerWriterDemo.cs&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/A&gt;&amp;nbsp;which is an implementation of a Reader-Writer lock.&amp;nbsp;&amp;nbsp; I held it up as an example of good design of a concurrent data structure.&amp;nbsp;&amp;nbsp; I want to now show you a bit of what my thinking was when it was designed and what the important properties it has.&amp;nbsp; &lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&lt;/SPAN&gt;Before you read the rest of this entry, you need to review the code carefully (It is not a lot of code).&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;&lt;?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Point 1: Follow the strict locking protocol whenever possible. &lt;/STRONG&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P&gt;The first point, is that once I created the spin lock, I could use it to follow the locking protocol described in my &lt;A href="http://msdn.microsoft.com/msdnmag/issues/05/08/Concurrency/default.aspx"&gt;concurrency article&lt;/A&gt;.&amp;nbsp; In particular I commented the exact set of memory that the spin lock protects, and I&amp;nbsp;very methodically entered the lock&amp;nbsp;before reading or writing that memory.&amp;nbsp;&amp;nbsp; In general I tried to follow the&amp;nbsp;'Monitor' protocol and Enter the lock&amp;nbsp;on entry to a public method and Exit&amp;nbsp;before leaving the method.&amp;nbsp; &lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P&gt;I did not do this completely uniformly, however.&amp;nbsp;&amp;nbsp; In the Release methods, I exit the lock before setting the event that wakes up any threads who went to sleep (see ExitAndWakeUpAppropriateWaiters).&amp;nbsp;&amp;nbsp; Why did I do this?&amp;nbsp;&amp;nbsp; It turns out I did not need to for correctness.&amp;nbsp; In fact if I had set the event while holding the lock the code would have been cleaner and easier to analyze.&amp;nbsp; The problem is performance on multi-processor machines.&amp;nbsp;&amp;nbsp; If I had set the event while holding the spin lock, it would have immediately caused a thread to wake up, and this thread would &lt;EM&gt;immediately&lt;/EM&gt; try to enter the spin lock (See the code in WaitOnEvent).&amp;nbsp; Since the thread that set the event still owns the spin lock,&amp;nbsp;the waking thread&amp;nbsp;would have to spin for a while.&amp;nbsp; This is unfortunate, and can be avoided by setting the event after leaving the spin lock.&lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Point 2: Scrutinize all work done outside a lock!&lt;/STRONG&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P&gt;ALWAYS be suspicious of work done outside of locks!&amp;nbsp; In this example, we do waiting on events, lazy initialization of events, and the setting of events all outside the protection of the spin lock and thus we need to be EXTRA careful reasoning about&amp;nbsp;relationships between state that is not&amp;nbsp;protected by&amp;nbsp;a common lock (in this case the relationship betwen the reader-writer lock state (eg. owner, numWriteWaiters ...),&amp;nbsp;and the state of the events (whether writeEvent is set or reset).&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P&gt;How to we be careful?&amp;nbsp;&amp;nbsp; Effectively we need a proof that the program has the properties we want (eg threads are waiting if and only if the reader-writer lock has been taken in a conflicting way by another thread).&amp;nbsp; The way these proofs work is by induction.&amp;nbsp;&amp;nbsp;&amp;nbsp;For &lt;EM&gt;sequential programs&lt;/EM&gt; find an interesting property that&amp;nbsp;is invariant over all state transitions (eg.&amp;nbsp; methods calls).&amp;nbsp; Stated another way, we need to find a property that holds when the object is constructed, and given that it holds before all possible method calls, we can show&amp;nbsp;that it holds after&amp;nbsp;that method&amp;nbsp;call.&amp;nbsp;&amp;nbsp; If this is true, then by induction, the property will hold for all time.&amp;nbsp; &lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P&gt;For &lt;EM&gt;concurrent programs&lt;/EM&gt; we have to modify the proof.&amp;nbsp;&amp;nbsp; Whenever the spin lock is released, other threads can cause state transitions.&amp;nbsp; Thus we need the property to hold &lt;EM&gt;whenever the spin lock is not held&lt;/EM&gt;.&amp;nbsp;&amp;nbsp; Because we release the locks in some places in the middle of methods, the property we choose must hold at these places too.&amp;nbsp;&lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P&gt;So the trick is to find these invariant properties, and sometimes it is not easy (which is why automated program validation is not a reality).&amp;nbsp; The good news, however is that once you find the invariant, validating it is usually a straightforward (although tedious and error prone) process.&amp;nbsp; In the case of our reader-writer lock&amp;nbsp;one property we need is something to insure that if a locks is waiting, it has a reason to.&amp;nbsp; Here is our first attempt (incorrect) at this invariant for the write event:&lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;UL type=disc&gt;
&lt;UL type=circle&gt;
&lt;LI class=MsoNormal style="MARGIN: 0in 0in 0pt; mso-list: l6 level2 lfo1; tab-stops: list 1.0in; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto"&gt;If writeEvent is reset then the reader-writer lock is in a state that would cause writers&amp;nbsp;to block&amp;nbsp;(namely the lock has been taken by some other thread).&amp;nbsp; &lt;o:p&gt;&lt;/o:p&gt;&lt;/LI&gt;&lt;/UL&gt;&lt;/UL&gt;
&lt;P&gt;Why did we pick this property?&amp;nbsp;&amp;nbsp; Because we know that threads only wait on an event if it is reset, this statement is pretty close to the property that we really want: that threads are not blocked unless they have reason to be).&amp;nbsp;&amp;nbsp;&amp;nbsp; Unfortunately, the property above is not true.&amp;nbsp; When the lock is first initialized we don't even have an event, but if we did it would probably be reset, which violates the condition above.&amp;nbsp;&amp;nbsp; Thinking about this, we realize that we don't care about the event in the non-contention case.&amp;nbsp; The purpose of the 'numWriters' variable is to indicate when the events matter (need to be set), so we modify the invariant to be&lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;UL type=disc&gt;
&lt;UL type=circle&gt;
&lt;LI class=MsoNormal style="MARGIN: 0in 0in 0pt; mso-list: l4 level2 lfo2; tab-stops: list 1.0in; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto"&gt;If &lt;SPAN style="COLOR: red"&gt;numWriters is nonzero and &lt;/SPAN&gt;the writeEvent is reset, then the reader-writer lock is in a state that would cause writers&amp;nbsp;to block&amp;nbsp;(namely the lock has been taken by some other thread).&amp;nbsp; &lt;o:p&gt;&lt;/o:p&gt;&lt;/LI&gt;&lt;/UL&gt;&lt;/UL&gt;
&lt;P&gt;&amp;nbsp;Let's see if we can prove it.&amp;nbsp;&amp;nbsp; Initially, numWriters is zero, so the property is trivially true.&amp;nbsp;&amp;nbsp; There is only one place we increment numWriters and reset the event, which is in the WaitOnEvent method, and right before we did this we validated that the reader-writer locks was in a condition that it needed to block writers.&amp;nbsp;&amp;nbsp; Thus our desired property is true when the spin lock is released (when the thread is about to call WaitOne).&amp;nbsp; Once the spin lock is released, other threads could race to do proceed with any work that can happen outside a lock, while our original thread proceeds to call 'WaitOne'.&amp;nbsp;&amp;nbsp;&amp;nbsp;We need to go through all the cases of what could happen after a myLock.Exit().&amp;nbsp;&amp;nbsp; This includes calling any API method as well as continuing from any place that myLock.Exit() was called in the middle of a method.&amp;nbsp; We need to go through these &lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Methods that could be called from other threads&lt;/STRONG&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;OL type=1&gt;
&lt;LI class=MsoNormal style="MARGIN: 0in 0in 0pt; mso-list: l2 level1 lfo3; tab-stops: list .5in; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto"&gt;AcquireReadLock could be called.&amp;nbsp; Since we get to assume our invariant is true, we know that numWriters is nonzero.&amp;nbsp; This means that AcquireReadLock will block (readers wait for writers), and thus no state transition will occur at all&lt;o:p&gt;&lt;/o:p&gt; 
&lt;LI class=MsoNormal style="MARGIN: 0in 0in 0pt; mso-list: l2 level1 lfo3; tab-stops: list .5in; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto"&gt;AquireWriteLock could be called.&amp;nbsp; By the same rationale, there is no state transition in this case either.&lt;o:p&gt;&lt;/o:p&gt; 
&lt;LI class=MsoNormal style="MARGIN: 0in 0in 0pt; mso-list: l2 level1 lfo3; tab-stops: list .5in; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto"&gt;ReleaseReaderLock is called.&amp;nbsp;&amp;nbsp; This is legal only&amp;nbsp;previously some thread aquired the read lock (owners &amp;gt; 0).&amp;nbsp; This does cause a state transition, if owners is still nonzero after the update, then writers should still wait (and thus our condition holds).&amp;nbsp; If owners == 0 after the transition, then the logic in ExitAndWakeUpAppropriateWaiters releases the spin lock and sets the writeEvent. &lt;o:p&gt;&lt;/o:p&gt;&lt;/LI&gt;&lt;/OL&gt;
&lt;P&gt;Opps!&amp;nbsp; At the instant the spin lock is released, the reader-writer lock is in a state where writers could enter, and the writeEvent is in the reset state!&amp;nbsp; Our condition does not hold!&amp;nbsp;&amp;nbsp;&amp;nbsp; We need to modify&amp;nbsp;our invariant and try again.&amp;nbsp; Let’s try &lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;UL type=disc&gt;
&lt;LI class=MsoNormal style="MARGIN: 0in 0in 0pt; mso-list: l0 level1 lfo4; tab-stops: list .5in; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto"&gt;If &lt;SPAN style="COLOR: black"&gt;and numWriters is nonzero &lt;/SPAN&gt;&lt;SPAN style="COLOR: red"&gt;and &lt;/SPAN&gt;the reader-writer lock is in a state that would writers&amp;nbsp;would &lt;SPAN style="COLOR: red"&gt;not&lt;/SPAN&gt; block&amp;nbsp;(that is the reader-writer lock is free), then writeEvent is set &lt;SPAN style="COLOR: red"&gt;or will eventually &lt;/SPAN&gt;be set.&lt;o:p&gt;&lt;/o:p&gt;&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;This is a weaker condition, but still gets us what we want (writers will not block indefinitely when the reader-writer lock is free), and IS true at the end of ExitAndWakeUpAppropriateWaiters&amp;nbsp;when the spin lock is released (since eventually the writeEvent.Set() will be called.&amp;nbsp; Thus this change seems to fix our problem.&amp;nbsp; However since we changed our invariant, we have to go back and check that all of our proofs that we have done to date still work with this weaker invariant (since you don't get to assume as much on entry).&amp;nbsp; It is left as an exercise to confirm this (:-).&amp;nbsp; &lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P&gt;OK we are not quite done yet, we still need to check the case of when ReleaseWriteLock() is called (it is effectively the same as the ReleaseReadLock() case), and that all the other places after we call myLock.Exit() are OK&amp;nbsp;&amp;nbsp;(in particular, before and after calling WaitOne, and before and after calling Set()).&amp;nbsp;&amp;nbsp;These all check out (please think about them).&amp;nbsp; &lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P&gt;Note that while we now have a nice invariant, it does not quite get us&amp;nbsp;the property we want which is &lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;UL type=disc&gt;
&lt;LI class=MsoNormal style="MARGIN: 0in 0in 0pt; mso-list: l3 level1 lfo5; tab-stops: list .5in; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto"&gt;&amp;nbsp;Writers never wait indefinitely when the lock is free.&lt;o:p&gt;&lt;/o:p&gt;&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;The reason is that we weakened the original invariant to only say something when 'numWaiters' is nonzero.&amp;nbsp; Thus we need another invariant&lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;UL type=disc&gt;
&lt;LI class=MsoNormal style="MARGIN: 0in 0in 0pt; mso-list: l5 level1 lfo6; tab-stops: list .5in; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto"&gt;numWaiters&amp;nbsp;is always greater than the number of threads that are calling 'writeEvent.WaitOne'.&amp;nbsp; &lt;o:p&gt;&lt;/o:p&gt;&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;This invariant is pretty easy to show&amp;nbsp;because we always increment this count right before waiting and decrement it after waiting.&amp;nbsp;&amp;nbsp; There is a time when it is an overestimation (we incremented it but did not call WaitOne yet), but it is never an underestimation.&amp;nbsp; &lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P&gt;At this point we can do our proof.&amp;nbsp; We do this 'by contradition', by assuming that the the negation and showing a contradition.&amp;nbsp;&amp;nbsp;&amp;nbsp;We start by assuming&amp;nbsp;that there&amp;nbsp;is a thread that is waiting forever but the lock is free.&amp;nbsp; That means that it was calling WaitOne, which means that numWaiters is nonzero (numWaiters is always bigger than the number of threads in WaitOne), which means that eventually Set() will be called (our invariant we showed earlier), which means that the thread should not be waiting (contradition since we assumed the thread was waiting forever).&amp;nbsp; Thus we have our proof. &lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P&gt;Phew!&amp;nbsp; Quite a lot of reasoning for such a small problem!&amp;nbsp; Now you can see why it pays to keep things simple.&amp;nbsp;&amp;nbsp; Some other interesting observations&lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;UL type=disc&gt;
&lt;LI class=MsoNormal style="MARGIN: 0in 0in 0pt; mso-list: l1 level1 lfo7; tab-stops: list .5in; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto"&gt;The proof above is not quite correct.&amp;nbsp; Since writeEvent was an AutoResetEvent, it does not follow that because&amp;nbsp;Set() was eventually called, that&amp;nbsp;the original thread should not be waiting.&amp;nbsp; AutoResetEvents only release one thread, not all threads waiting.&amp;nbsp;&amp;nbsp;&amp;nbsp;In fact the statement we are trying to prove is not true only a weaker statement is true&lt;o:p&gt;&lt;/o:p&gt; &lt;/LI&gt;&lt;/UL&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; mso-list: l1 level1 lfo7; tab-stops: list .5in; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto"&gt;
&lt;UL type=circle&gt;
&lt;LI class=MsoNormal style="MARGIN: 0in 0in 0pt; mso-list: l1 level2 lfo7; tab-stops: list 1.0in; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto"&gt;If the reader-writer lock is free, at least one writer (a thread which called AquireWriterLock but not ReleaseWriterLock), is unblocked. &lt;o:p&gt;&lt;/o:p&gt;&lt;/LI&gt;&lt;/UL&gt;&lt;/P&gt;
&lt;P&gt;This property is good enough, since only one writer can make progress anyway, but it shows that you have to be careful..&amp;nbsp;&amp;nbsp;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;UL type=disc&gt;
&lt;LI class=MsoNormal style="MARGIN: 0in 0in 0pt; mso-list: l7 level1 lfo8; tab-stops: list .5in; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto"&gt;In ExitAndWakeUpAppropriateWaiters it would be tempting to exit the spin lock at the beginning of the method and then do the checking to see if the events needed to be set.&amp;nbsp; This would break the code however.&amp;nbsp; The problem is that the instant the spin lock is released, the reader writer variables can change and thus you might not set the event when you need to.&amp;nbsp; It is a good exercise to find where the proof breaks down in this case. &lt;o:p&gt;&lt;/o:p&gt;
&lt;LI class=MsoNormal style="MARGIN: 0in 0in 0pt; mso-list: l7 level1 lfo8; tab-stops: list .5in; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto"&gt;We only handled the case for the write event. We need to repeat the process for the read event.&amp;nbsp; Luckily the logic is almost identical.&lt;o:p&gt;&lt;/o:p&gt;&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;&lt;B style="mso-bidi-font-weight: normal"&gt;&lt;SPAN style="FONT-SIZE: 14pt"&gt;Recap&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/B&gt;&lt;/P&gt;
&lt;P&gt;I apologize for this blog getting so technical.&amp;nbsp; To do it justice you really need to work though the proof yourself looking over the code carefully.&amp;nbsp;&amp;nbsp; This will take hours to do properly.&amp;nbsp;&amp;nbsp; I realize that most of you will not do this.&amp;nbsp; This is OK.&amp;nbsp; However those who are not willing to do this kind of analysis should NOT be writing code like MyReaderWriterLock.&amp;nbsp; It is too easy to get this stuff wrong.&amp;nbsp; &lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P&gt;Finally, we have not even talked about fairness issues and reentrancy.&amp;nbsp; Stay tuned for more on that.&amp;nbsp; &lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="tab-stops: center 3.0in"&gt;&amp;nbsp;&lt;SPAN style="mso-tab-count: 1"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=564854" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/vancem/archive/tags/Concurrency+/default.aspx">Concurrency </category></item><item><title>Low-Lock Techniques in action: Implementing a Reader-Writer lock</title><link>http://blogs.msdn.com/vancem/archive/2006/03/28/563180.aspx</link><pubDate>Tue, 28 Mar 2006 18:40:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:563180</guid><dc:creator>vancem</dc:creator><slash:comments>17</slash:comments><comments>http://blogs.msdn.com/vancem/comments/563180.aspx</comments><wfw:commentRss>http://blogs.msdn.com/vancem/commentrss.aspx?PostID=563180</wfw:commentRss><description>&lt;P&gt;In my article &lt;SPAN class=MsoHyperlink&gt;&lt;SPAN style="FONT-SIZE: 10pt; COLOR: windowtext; FONT-FAMILY: Verdana; TEXT-DECORATION: none; text-underline: none"&gt;&lt;A href="http://msdn.microsoft.com/msdnmag/issues/05/08/Concurrency/default.aspx"&gt;What Every Dev Must Know About Multithreaded Apps&lt;/A&gt;&lt;/SPAN&gt;&lt;/SPAN&gt; I discuss the fundamental principles of using locks correctly.&amp;nbsp; In that article I strongly encourage the use of reader-writer locks&amp;nbsp;because these locks create the protection you need (insuring that readers and writers don't conflict), while potentially allowing significantly more concurrency to take place (by allowing multiple readers to enter the lock simultaneously).&amp;nbsp;&amp;nbsp;&amp;nbsp; I did not have space in that article to discuss reader-writer locks in detail however.&amp;nbsp; I would like to start correcting that here. &lt;/P&gt;
&lt;P&gt;The .NET Runtime implementation of a reader-writer lock is System.Threading.ReaderWriterLock.&amp;nbsp; It is really a pretty simple API at its heart (ignore stuff about LockCookies, it is there to support nesting of locks, which you should probably avoid anyway).&amp;nbsp; Here is a sample use&lt;?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;P&gt;MyReaderWriterLock myLock = new MyReaderWriterLock();&lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P&gt;myLock.AcquireReaderLock(Timeout.Infinite);&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// No writers can be in here (AquireWriterLock will block)&lt;BR&gt;myLock.ReleaseReaderLock();&lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P&gt;myLock.AcquireWriterLock(Timeout.Infinite);&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// No readers or other writers can be in here (Aquire*Lock blocks)&lt;BR&gt;myLock.ReleaseWriterLock();&lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;It is pretty straightforward to convert a program from using ordinary locks into using reader-writer locks (for those blocks that only read use AcquireReaderLock, otherwise use AquireWriterLock).&lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P&gt;This is all very nice.&amp;nbsp; Unfortunately, there is a problem.&amp;nbsp; The current .NET Runtime implementation of ReaderWriterLock is about 8 times slower than System.Monitor (normal locks), in the common case where there is no lock contention.&amp;nbsp;&amp;nbsp; This is quite unfortunate, as it acts as a disincentive for doing the right thing (and using reader-writer locks).&amp;nbsp; Microsoft is likely to fix this in the next release, however what do you do in the mean time?&lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Implementing a Reader-Writer Lock&lt;/STRONG&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P&gt;The obvious answer is to implement an efficient 'drop in' version of ReaderWriterLock that you can use until Microsoft provides an better one.&amp;nbsp; This is what this article is all about.&amp;nbsp; Sadly, if&amp;nbsp;our goal is to be as efficient as System.Montor, we can't use the standard locking protocol&amp;nbsp;to implement a Reader-Writer Lock since&amp;nbsp;every operation would have to lock and unlock, making it at least twice as slow as System.Monitor.&amp;nbsp; Thus we are forced to look into 'Low Lock Techniques', for an answer.&amp;nbsp; &amp;nbsp;&lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P&gt;In my article &lt;SPAN style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: Verdana"&gt;&lt;A href="http://msdn.microsoft.com/msdnmag/issues/05/10/MemoryModels/default.aspx"&gt;Understand the Impact of Low-Lock Techniques in Multithreaded Apps&lt;/A&gt;&amp;nbsp;I list the techniques that &lt;EM&gt;&lt;SPAN style="FONT-FAMILY: Verdana"&gt;if used very carefully&lt;/SPAN&gt;&lt;/EM&gt;, can sometimes be applied to do very efficient multi-threaded synchronization.&amp;nbsp; I also mention that these techniques should only be used when the use of standard locks are problematic (usually because of performance issues).&amp;nbsp; Reader-Writer locks fall into this problematic cataegory.&lt;/SPAN&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;SPAN style="COLOR: black"&gt;Possible Low lock techniques to use&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: Verdana"&gt;In the article the techniques are listed in order of decreasing utility, and are &lt;/SPAN&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;OL type=1&gt;
&lt;LI class=MsoNormal style="MARGIN: 0in 0in 0pt; mso-list: l1 level1 lfo1; tab-stops: list .5in; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto"&gt;&lt;SPAN style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: Verdana"&gt;Avoid locks on reads&lt;/SPAN&gt;&lt;o:p&gt;&lt;/o:p&gt; 
&lt;LI class=MsoNormal style="MARGIN: 0in 0in 0pt; mso-list: l1 level1 lfo1; tab-stops: list .5in; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto"&gt;&lt;SPAN style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: Verdana"&gt;Spin Locks&lt;/SPAN&gt;&lt;o:p&gt;&lt;/o:p&gt; 
&lt;LI class=MsoNormal style="MARGIN: 0in 0in 0pt; mso-list: l1 level1 lfo1; tab-stops: list .5in; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto"&gt;&lt;SPAN style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: Verdana"&gt;Raw Interlocked operations&lt;/SPAN&gt;&lt;o:p&gt;&lt;/o:p&gt; 
&lt;LI class=MsoNormal style="MARGIN: 0in 0in 0pt; mso-list: l1 level1 lfo1; tab-stops: list .5in; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto"&gt;&lt;SPAN style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: Verdana"&gt;Lazyinit&lt;/SPAN&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/LI&gt;&lt;/OL&gt;
&lt;P&gt;&lt;SPAN style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: Verdana"&gt;Because the important APIs for a reader-writer lock always do an update, technique 1 is not applicable.&amp;nbsp; Thus our best bet is Spin locks (note the later two techniques are not NEARLY as useful as the first two).&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;SPAN style="COLOR: black"&gt;Implementing a spin locks&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: Verdana"&gt;It turns out that spin locks have some very nice properties.&amp;nbsp; Namely they ARE locks,&amp;nbsp;so all the nice properties of locks apply (once you take the lock you have mutual exclusion and reasoning about the program gets&amp;nbsp;MUCH easier).&amp;nbsp; They are also efficient (one interlocked operation per Enter-Exit pair).&amp;nbsp;&amp;nbsp;Thus we should be able to implement a Reader-writer lock with only one interlocked operation per method call, which is the same as System.Monitor (which also has one interlocked operation per method call).&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: Verdana"&gt;Please refer to the code attached to this post &lt;A href="/vancem/attachment/563180.ashx"&gt;readerWriterDemo.cs&lt;/A&gt;.&amp;nbsp;&amp;nbsp;The first class is the spin lock.&amp;nbsp; It has an 'Enter' and 'Exit' API.&amp;nbsp; For more information on spin locks&amp;nbsp;see my low-lock article or Jeff Richter's article &lt;/SPAN&gt;&lt;A href="http://msdn.microsoft.com/msdnmag/issues/05/10/ConcurrentAffairs/"&gt;Concurrent Affairs&lt;/A&gt;.&amp;nbsp; The implementation in &lt;A href="/vancem/attachment/563180.ashx"&gt;readerWriterDemo.cs&lt;/A&gt; deals with all the subtle details you need to worry about.&amp;nbsp; It might need some tuning on how long it spins, but otherwise it is a very respectable implementation and you should feel free to use it if you need a spin lock yourself.&amp;nbsp;&amp;nbsp; The&amp;nbsp;only caveat I curretly have&amp;nbsp;is that I have&amp;nbsp;commented out calls to Thread.BeginCriticalRegion and Thread.EndCriticalRegion because they are relatively expensive (they make my perf numbers look bad), and&amp;nbsp;is only needed in environments like SQL Server where threads may be aborted at any instant.&amp;nbsp; If a thread is aborted while the spin lock is held, the SQL Server host&amp;nbsp; wants to know so that it can kill the entire appdomain instead of just the thread.&amp;nbsp;&amp;nbsp; If you are not in&amp;nbsp;SQL Server you can get away without this.&lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P&gt;Before using the spin lock, we need to confirm that our read-writer lock is a good candidate for its use.&amp;nbsp; To be a good candidate is must be the case that the time the lock is held is very short (measured in at most dozens of instructions).&amp;nbsp;&amp;nbsp; We believe that this is the case since all of our API for the reader-writer lock do little and are perf critical, so the code paths NEED to be short.&amp;nbsp; However after we get our implementation we need to confirm that we don't have rare paths that might hold the lock for a long time.&amp;nbsp; It turns out with a bit of work we can make certain this is the case.&amp;nbsp; &lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;SPAN style="COLOR: black"&gt;Implementing a reader-writer lock with the spin lock &lt;/SPAN&gt;&lt;/STRONG&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P&gt;With a spin lock in hand, writing the reader-writer lock is not too much code (~ 150 lines, with comments), and hopefully is easy to understand.&amp;nbsp; Every API takes a lock, does work, and leaves.&amp;nbsp; To deal with the case where you must wait, we need OS structures to wait on (eg ManualResetEvent, AutoResetEvent).&amp;nbsp;&amp;nbsp; When a ManualResetEvent is 'Set' all threads waiting will wake up.&amp;nbsp; This is appropriate for readers.&amp;nbsp; For writers, it makes more sense to only wake up the first thread, and this is what AutoResetEvent does.&amp;nbsp;&amp;nbsp; The code has a lot of comments, so please take a look.&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; There are actually a lot of subtleties to think about (what happens in various error conditions, what APIs can fail, how efficient the lock is in various scenarios, how long we are holding the spin lock ...).&amp;nbsp; I point out some of these subtleties in comments, however the really important point is that the code itself is short and straightforward.&amp;nbsp; This is CRITICAL because the more code you have, the more difficult it is to confirm the code works in all the subtle cases.&amp;nbsp; It pays to keep things simple.&amp;nbsp; &lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;SPAN style="COLOR: black"&gt;Performance Results of the reader-writer lock&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P&gt;If you run the program, it will measure the performance of an Enter/Exit pair.&amp;nbsp; Here is what I got.&amp;nbsp; &lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;&lt;SPAN style="FONT-SIZE: 7.5pt"&gt;10Meg Enter/Exit&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 371.6953 msec = 59.61 CPU clocks per Enter/Exit&lt;BR&gt;10Meg RW Locks (.NET library) 3175.8594 msec = 8.5X monitor time&lt;BR&gt;10Meg New RW Locks&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 428.8234 msec = 1.2X monitor time&lt;/SPAN&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;As you can see, on my machine, the .NET library is 8.5X slower than System.Monitor, but the new RW locks are only&amp;nbsp;20% slower.&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;With some more tuning I suspect&amp;nbsp;I can get even closer,&amp;nbsp;however it will make the code more complicated, so I don't want to do that in this published code (cleanliness is worth something).&amp;nbsp; &lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P&gt;As written, the lock has the property that if a writer is waiting for the locks, new readers can't come in and block it indefinitely.&amp;nbsp; Instead the readers wait until all the other readers drain, and the writer gets its turn.&amp;nbsp; The lock is unfair in the sense that when a lock is released it is not guaranteed that the first waiter gets the lock.&amp;nbsp;&amp;nbsp; Thus starvation is possible (however unlikely).&amp;nbsp; It turns out that fairness causes performance issues (I will discuss in a later blog), so the current unfair protocol has better performance characteristics in almost all cases.&amp;nbsp;&amp;nbsp; It is not too hard to&amp;nbsp;change the implementation to be fair, and I might take that up in a later blog (please request it if you are interested). &amp;nbsp;&lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P&gt;The lock as written is also not reentrant.&amp;nbsp; It is relatively easy and efficient to add this (although it is not clear this feature is a good idea), and I show how to do that too (please request it if you are interested).&amp;nbsp; &lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P&gt;The code is really worth looking at carefully and I encourage you to do so.&amp;nbsp; It is small enough that you can understand the intent quickly, but there are many scenarios to think about.&amp;nbsp;&amp;nbsp;&lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;SPAN style="FONT-SIZE: 18pt"&gt;Recap&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P&gt;What are the take-aways?&lt;o:p&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;OL type=1&gt;
&lt;LI class=MsoNormal style="MARGIN: 0in 0in 0pt; mso-list: l0 level1 lfo2; tab-stops: list .5in; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto"&gt;Use reader-writer locks as suggested in my first article.&amp;nbsp; If lock perf is a problem, take the implementation here as a 'drop in' replacement for the .NET System.ReaderWriterLock.&amp;nbsp; Feel free to use this implementation until Microsoft fixes the version in its library.&amp;nbsp; 
&lt;LI class=MsoNormal style="MARGIN: 0in 0in 0pt; mso-list: l0 level1 lfo2; tab-stops: list .5in; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto"&gt;Spin-locks are a really valuable low lock technique.&amp;nbsp;&amp;nbsp; There were used here to make a clean, simple but efficient reader-writer lock written competely in managed code.&amp;nbsp;&amp;nbsp; This lock is &lt;EM&gt;significantly&lt;/EM&gt; simpler than most implementations I have seen, yet is clost to optimal.&amp;nbsp; The reason for this is that the use of spin locks GREATLY simplifies the design and analysis of the lock, without sacrificing performance.&amp;nbsp;&amp;nbsp; Feel free to use the Spin lock implementation show here to make other high performance concurrent constructs.&amp;nbsp; &lt;o:p&gt;&lt;/o:p&gt;
&lt;LI class=MsoNormal style="MARGIN: 0in 0in 0pt; mso-list: l0 level1 lfo2; tab-stops: list .5in; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto"&gt;Even something as simple as a Reader-Writer lock has subtleties about its behavior (especially when you factor in performance concerns).&amp;nbsp;&amp;nbsp; Keeping it simple really pays off here.&amp;nbsp; &lt;o:p&gt;&lt;/o:p&gt;&lt;/LI&gt;&lt;/OL&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=563180" width="1" height="1"&gt;</description><enclosure url="http://blogs.msdn.com/vancem/attachment/563180.ashx" length="10633" type="text/plain" /><category domain="http://blogs.msdn.com/vancem/archive/tags/Perf/default.aspx">Perf</category><category domain="http://blogs.msdn.com/vancem/archive/tags/Concurrency+/default.aspx">Concurrency </category></item><item><title>Two articles on concurrent programming I wrote</title><link>http://blogs.msdn.com/vancem/archive/2006/02/20/535735.aspx</link><pubDate>Tue, 21 Feb 2006 01:24:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:535735</guid><dc:creator>vancem</dc:creator><slash:comments>0</slash:comments><comments>http://blogs.msdn.com/vancem/comments/535735.aspx</comments><wfw:commentRss>http://blogs.msdn.com/vancem/commentrss.aspx?PostID=535735</wfw:commentRss><description>&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;This is just a quick plug for two articles that I wrote for MSDN magazine a few months back. &lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&lt;/SPAN&gt;They are&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: Verdana"&gt;&lt;A href="http://msdn.microsoft.com/msdnmag/issues/05/08/Concurrency/default.aspx"&gt;&lt;SPAN style="FONT-FAMILY: 'Times New Roman'"&gt;&lt;?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/A&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;OL style="MARGIN-TOP: 0in" type=1&gt;
&lt;LI class=MsoNormal style="MARGIN: 0in 0in 0pt; mso-list: l0 level1 lfo1; tab-stops: list .5in"&gt;&lt;SPAN class=MsoHyperlink&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana"&gt;&lt;A href="http://msdn.microsoft.com/msdnmag/issues/05/08/Concurrency/default.aspx"&gt;What Every Dev Must Know About Multithreaded Apps&lt;/A&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 10pt"&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI class=MsoNormal style="MARGIN: 0in 0in 0pt; mso-list: l0 level1 lfo1; tab-stops: list .5in"&gt;&lt;SPAN style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: Verdana"&gt;&lt;A href="http://msdn.microsoft.com/msdnmag/issues/05/10/MemoryModels/default.aspx"&gt;Understand the Impact of Low-Lock Techniques in Multithreaded Apps&lt;/A&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 10pt"&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/LI&gt;&lt;/OL&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: Verdana"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: Verdana"&gt;The first article, as its title suggests, is what I think every programmer should know about Multithreaded programming.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;I know from my own ramp up on multi-threading issues that while the principles of concurrent programming are actually relatively simple, the ramifications of those principles are VERY subtle. &lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&lt;/SPAN&gt;The upshot is that if you write multi-threaded apps (and it turns out that EVERY managed app that uses finalizers is multi-threaded), you should take a look at the first article (either you will already know it (which is good), or you will be glad you learned it.&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: Verdana"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: Verdana"&gt;The second article is really trying to warn you way from what is often called ‘lock free’ programming. &lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&lt;/SPAN&gt;If concurrent programming is subtle, concurrent lock free programming is 10 times as subtle. &lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;The article is mostly an exposition of SOME of the subtleties’, and hopefully that will be enough to scare you away from using lock free techniques in all but the most constrained circumstances.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: Verdana"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: Verdana"&gt;Enjoy the articles.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 10pt"&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=535735" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/vancem/archive/tags/Concurrency+/default.aspx">Concurrency </category></item></channel></rss>