http://www.infinitec.de/InfiniTec - Henning Krauses Blog - Multithreading library2007-01-28T11:00:00+00:00Don't adjust your mind - it's reality that is malfunctioning Henning KrauseBlogEngine.Net Syndication Generatorhttp://www.infinitec.de/opml.axdHenning KrauseDon't adjust your mind - it's reality that is malfunctioning en-USInfiniTec - Henning Krauses Blog 0.0000000.000000http://www.infinitec.de/post/2007/01/28/Minor-update-on-my-multi-threading-library.aspxMinor update on my multi threading library2007-01-27T23:00:00+00:00hkrause<div xmlns="http://www.w3.org/1999/xhtml"><p>Here is a small update on my multi threading framework. After some background reading on how the <a href="http://msdn2.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx">BackgroundWorker</a> synchronizes event invocations, I decided to implement a similar functionality into my framework as well.</p><h3>UI Synchronization</h3><div><p align="left">The Background worker allows updates to the UI without the need to call <a href="http://msdn2.microsoft.com/en-us/library/zyzhdc6b.aspx">Control.Invoke</a>. It does this by utilizing the <a href="http://msdn2.microsoft.com/en-us/library/system.threading.synchronizationcontext.aspx">SynchronizationContext</a> class (A good article on this topic can be found <a href="http://msdn2.microsoft.com/en-us/library/system.threading.synchronizationcontext.aspx">here</a> on CodeProject).</p><p align="left">This version of my multithreading framework also uses this mechanism to call the Completed, ProgressChanged and Error events. And best of all, this is agnostic to the UI framework used: It works with Windows Forms as well as Windows Presentation Foundation.</p></div><h3>Locking inside an asynchronous method</h3><div><p>Another issue you should know when working with my framework is this: You must never return a NestedOperation or a ParallelOperaton from within a region protected by either a <a href="http://msdn2.microsoft.com/en-us/library/system.threading.monitor.aspx">Monitor</a> (explicitly or via the <strong>lock</strong> (<strong>SyncLock</strong> in Visual Basic) keyword) or a <a href="http://msdn2.microsoft.com/en-us/library/system.threading.mutex.aspx">Mutex</a>. This is because these synchronization primitives are thread-dependent. Use a <a href="http://msdn2.microsoft.com/en-us/library/system.threading.semaphore.aspx">Semaphore</a> instead.</p><p>Consider the following code:</p><div class="clsCode" style="FONT-SIZE: 10pt; BACKGROUND-COLOR: #f0f0f0"><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 1</span> <span style="color: #0000ff;">if</span> (_ConfigurationNamingContext == <span style="color: #0000ff;">null</span>)</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 2</span> {</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 3</span> <span style="color: #0000ff;">lock</span> (_ConfigurationNamingContextLock)</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 4</span> {</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 5</span> <span style="color: #0000ff;">if</span> (_ConfigurationNamingContext == <span style="color: #0000ff;">null</span>)</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 6</span> {</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 7</span> <span style="color: #0000ff;">using</span> (</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 8</span> <span style="color: #2b91af;">AsyncOperation</span><<span style="color: #0000ff;">int</span>, <span style="color: #2b91af;">Item</span>> operation =</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 9</span> <span style="color: #2b91af;">ActiveDirectoryEntry</span>.GetConfigurationNamingContext(_Connection.ActiveDirectoryConnection)</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 10</span> )</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 11</span> {</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 12</span> <span style="color: #0000ff;">yield</span><span style="color: #0000ff;">return</span><span style="color: #0000ff;">new</span><span style="color: #2b91af;">NestedOperation</span>(operation, <span style="color: #0000ff;">false</span>);</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 13</span> _ConfigurationNamingContext = operation.Result;</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 14</span> }</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 15</span> }</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 16</span> }</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 17</span> }</span></p></div><p>The code from line 3 through line 16 are protected by a Monitor to ensure that the code is not executed twice (which could hapen when the method is called in parellel). In line 12, a nested operation is executed, doing the hard work. But when the displayed method is resumed in line 13, the code may run on a different thread than before. This will cause the Monitor.Exit (which is implicitly called in line 16) to throw a <a href="http://msdn2.microsoft.com/en-us/library/system.threading.synchronizationlockexception.aspx">SynchronizationLockException</a>.</p><p>To avoid this, use a Semaphore instead.</p><p>Create a semaphore with a maximum count of 1:</p><div class="clsCode" style="FONT-SIZE: 10pt; BACKGROUND-COLOR: #f0f0f0"><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 1</span> <span style="color: #0000ff;">private</span><span style="color: #0000ff;">static</span><span style="color: #2b91af;">Semaphore</span> _ConfigurationNamingContextLock = <span style="color: #0000ff;">new</span><span style="color: #2b91af;">Semaphore</span>(1, 1);</span></p></div><p>Now, use the semaphore as outlined here:</p><div class="clsCode" style="FONT-SIZE: 10pt; BACKGROUND-COLOR: #f0f0f0"><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 1</span> <span style="color: #0000ff;">if</span> (_ConfigurationNamingContext == <span style="color: #0000ff;">null</span>)</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 2</span> {</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 3</span> <span style="color: #0000ff;">try</span></span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 4</span> {</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 5</span> _ConfigurationNamingContextLock.WaitOne();</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 6</span> </span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 7</span> <span style="color: #0000ff;">if</span> (_ConfigurationNamingContext == <span style="color: #0000ff;">null</span>)</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 8</span> {</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 9</span> <span style="color: #0000ff;">using</span> (</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 10</span> <span style="color: #2b91af;">AsyncOperation</span><<span style="color: #0000ff;">int</span>, <span style="color: #2b91af;">Item</span>> operation =</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 11</span> <span style="color: #2b91af;">ActiveDirectoryEntry</span>.GetConfigurationNamingContext(_Connection.ActiveDirectoryConnection)</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 12</span> )</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 13</span> {</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 14</span> <span style="color: #0000ff;">yield</span><span style="color: #0000ff;">return</span><span style="color: #0000ff;">new</span><span style="color: #2b91af;">NestedOperation</span>(operation, <span style="color: #0000ff;">false</span>);</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 15</span> _ConfigurationNamingContext = operation.Result;</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 16</span> }</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 17</span> }</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 18</span> }</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 19</span> <span style="color: #0000ff;">finally</span></span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 20</span> {</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 21</span> _ConfigurationNamingContextLock.Release();</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 22</span> }</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 23</span> }</span></p></div><p>It's not as neat as using the <strong>lock</strong> keyword, but it works :-)</p></div></div><h2 xmlns="http://www.w3.org/1999/xhtml">Change log for version 2.5.1</h2><div xmlns="http://www.w3.org/1999/xhtml"><ul style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px" type="disc"><li>Events are now called via a SynchronizationContext. This makes the usage of Invoke() methods unnecessary when reporting progress to Windows Forms or WPF applications.</li></ul></div><h2 xmlns="http://www.w3.org/1999/xhtml">Downloads</h2><table xmlns="http://www.w3.org/1999/xhtml"><tr><td><img style="border:none; vertical-align: middle;" src=file.axd?file=infinitec.threading_2_5_1/zip.gif /></td><td><a href=file.axd?file=infinitec.threading_2_5_1/InfiniTec.Threading.zip>InfiniTec.Threading.zip</a> (277,385 Bytes)<br />Source, Binaries and Documentation for version 2.5.1</td></tr></table>2007-01-27T23:00:00+00:00hkrauseHere is a small update on my multi threading framework. After some background reading on how the BackgroundWorker synchronizes event invocations, I decided to implement a similar functionality into my framework as well.The Background worker allows updates to the UI without the need to call Control.Invoke. It does this by utilizing the SynchronizationContext class (A good article on this topic can be found here on CodeProject).This version of my multithreading framework also uses this mechanism to call the Completed, ProgressChanged and Error events. And best of all, this is agnostic to the UI framework used: It works with Windows Forms as well as Windows Presentation Foundation.Another issue you should know when working with my framework is this: You must never return a NestedOperation or a ParallelOperaton from within a region protected by either a Monitor (explicitly or via the lock (SyncLock in Visual Basic) keyword) or a Mutex. This is because these synchronization primitives are thread-dependent. Use a Semaphore instead.Consider the following code: 1 if (_ConfigurationNamingContext == null) 2 { 3 lock (_ConfigurationNamingContextLock) 4 { 5 if (_ConfigurationNamingContext == null) 6 { 7 using ( 8 AsyncOperation<int, Item> operation = 9 ActiveDirectoryEntry.GetConfigurationNamingContext(_Connection.ActiveDirectoryConnection) 10 ) 11 { 12 yieldreturnnewNestedOperation(operation, false); 13 _ConfigurationNamingContext = operation.Result; 14 } 15 } 16 } 17 }The code from line 3 through line 16 are protected by a Monitor to ensure that the code is not executed twice (which could hapen when the method is called in parellel). In line 12, a nested operation is executed, doing the hard work. But when the displayed method is resumed in line 13, the code may run on a different thread than before. This will cause the Monitor.Exit (which is implicitly called in line 16) to throw a SynchronizationLockException.To avoid this, use a Semaphore instead.Create a semaphore with a maximum count of 1: 1 privatestaticSemaphore _ConfigurationNamingContextLock = newSemaphore(1, 1);Now, use the semaphore as outlined here: 1 if (_ConfigurationNamingContext == null) 2 { 3 try 4 { 5 _ConfigurationNamingContextLock.WaitOne(); 6 7 if (_ConfigurationNamingContext == null) 8 { 9 using ( 10 AsyncOperation<int, Item> operation = 11 ActiveDirectoryEntry.GetConfigurationNamingContext(_Connection.ActiveDirectoryConnection) 12 ) 13 { 14 yieldreturnnewNestedOperation(operation, false); 15 _ConfigurationNamingContext = operation.Result; 16 } 17 } 18 } 19 finally 20 { 21 _ConfigurationNamingContextLock.Release(); 22 } 23 }It's not as neat as using the lock keyword, but it works :-)http://www.infinitec.de/pingback.axdhttp://www.infinitec.de/post.aspx?id=70a90719-081b-43a3-bde0-ae13ba9fdf430http://www.infinitec.de/trackback.axd?id=70a90719-081b-43a3-bde0-ae13ba9fdf43http://www.infinitec.de/post/2007/01/28/Minor-update-on-my-multi-threading-library.aspx#commenthttp://www.infinitec.de/syndication.axd?post=70a90719-081b-43a3-bde0-ae13ba9fdf43http://www.infinitec.de/post/2007/01/27/Extreme-threading.aspxExtreme threading...2007-01-26T23:00:00+00:00hkrause<div xmlns="http://www.w3.org/1999/xhtml"><p>This is a major upgrade for my multithreading library. If you are not familiar about what this library does, please read the other articles in <a href="libraries/threading">this section</a>. </p><p>New in this release are parallel operations: You can return ParallelOperation from within an asynchronous operation. The execution of the current operation will be suspended until all nested operations are executed. Additionally, a maximum number of parallel executions can be passed along, which allows you to further control the execution of the nested operations. And of course, you can select which exceptions you want to catch on each of the nested operation.</p><p>I'm primarily using these parallel operations in my <a href="libraries/exchange">InfiniTec.Exchange</a> library to speed up lookups to Active Directory and the Exchange store. For example, when looking up members of a distribution list, I get a bunch of references, either to user in Active Directory, or references to contacts on an Exchange folder. Take a look at this method:</p><div class="clsCode" style="FONT-SIZE: 10pt; BACKGROUND-COLOR: #f0f0f0"><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 1</span> <span style="color: #0000ff;">private</span><span style="color: #2b91af;">IEnumerable</span><<span style="color: #2b91af;">OperationAction</span>> RefreshInternal(<span style="color: #2b91af;">AsyncOperation</span> baseOperation)</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 2</span> {</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 3</span> <span style="color: #2b91af;">Dictionary</span><<span style="color: #2b91af;">NestedOperation</span>,<span style="color: #2b91af;">EntryIdReference</span>> operations;</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 4</span> <span style="color: #2b91af;">ExchangeStoreReference</span> entryId;</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 5</span> <span style="color: #2b91af;">List</span><<span style="color: #2b91af;">IAddressListEntry</span>> result;</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 6</span> </span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 7</span> _UnresolvableEntries = 0;</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 8</span> </span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 9</span> <span style="color: #0000ff;">yield</span><span style="color: #0000ff;">return</span><span style="color: #0000ff;">new</span><span style="color: #2b91af;">NestedOperation</span>(baseOperation, <span style="color: #0000ff;">false</span>);</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 10</span> </span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 11</span> </span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 12</span> <span style="color: #0000ff;">if</span> (!_ResolveMembers)</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 13</span> {</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 14</span> _ResolveMembers = <span style="color: #0000ff;">true</span>;</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 15</span> <span style="color: #0000ff;">yield</span><span style="color: #0000ff;">break</span>;</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 16</span> }</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 17</span> </span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 18</span> entryId = <span style="color: #0000ff;">new</span><span style="color: #2b91af;">ExchangeStoreReference</span>(Properties.GetProperty<<span style="color: #0000ff;">byte</span>[]>(<span style="color: #2b91af;">WellknownProperties</span>.<span style="color: #2b91af;">Item</span>.EntryId).Value);</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 19</span> </span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 20</span> operations = <span style="color: #0000ff;">new</span><span style="color: #2b91af;">Dictionary</span><<span style="color: #2b91af;">NestedOperation</span>, <span style="color: #2b91af;">EntryIdReference</span>>();</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 21</span> </span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 22</span> <span style="color: #0000ff;">foreach</span> (<span style="color: #2b91af;">EntryIdReference</span> reference <span style="color: #0000ff;">in</span> DecodeMembers(entryId.StoreId))</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 23</span> {</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 24</span> operations.Add(</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 25</span> <span style="color: #0000ff;">new</span><span style="color: #2b91af;">NestedOperation</span>(reference.GetResolveOperation(BaseItem.Connection), <span style="color: #0000ff;">false</span>),</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 26</span> reference);</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 27</span> }</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 28</span> </span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 29</span> <span style="color: #0000ff;">yield</span><span style="color: #0000ff;">return</span><span style="color: #0000ff;">new</span><span style="color: #2b91af;">ParallelOperations</span>(operations.Keys, 5);</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 30</span> </span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 31</span> result = <span style="color: #0000ff;">new</span><span style="color: #2b91af;">List</span><<span style="color: #2b91af;">IAddressListEntry</span>>();</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 32</span> </span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 33</span> <span style="color: #0000ff;">foreach</span> (<span style="color: #2b91af;">KeyValuePair</span><<span style="color: #2b91af;">NestedOperation</span>, <span style="color: #2b91af;">EntryIdReference</span>> entry <span style="color: #0000ff;">in</span> operations)</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 34</span> {</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 35</span> <span style="color: #2b91af;">AsyncOperation</span><<span style="color: #2b91af;">IAddressListEntry</span>> operation;</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 36</span> </span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 37</span> operation = (<span style="color: #2b91af;">AsyncOperation</span><<span style="color: #2b91af;">IAddressListEntry</span>>) entry.Key.Operation;</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 38</span> <span style="color: #0000ff;">if</span> (operation.StatusInformation != <span style="color: #0000ff;">null</span>)</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 39</span> {</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 40</span> result.Add(operation.StatusInformation);</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 41</span> }</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 42</span> <span style="color: #0000ff;">else</span></span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 43</span> {</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 44</span> _UnresolvableEntries++;</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 45</span> }</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 46</span> }</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 47</span> </span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 48</span> _Members = result.AsReadOnly();</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 49</span> }</span></p></div><p>In Line 9, I call the original refresh method, using the NestedOperation operation action. After that, I decode those member entries in lines 22 to 27 and put them in a dictionary. The interesting thing happens in line 29. At this point, I return a ParallelOperation operation action with a list of those member references. Additionally, a maximum number of concurrent operations of 5 is specified. </p><p>When the method resumes it's operation in line 31, all nested operations have been completed.</p></div><h2 xmlns="http://www.w3.org/1999/xhtml">Changelog</h2><div xmlns="http://www.w3.org/1999/xhtml"><ul style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px" type="disc"><li>Introduced parallel operations: Using the ParallelOperations action, you can now return multiple nested operations which will be executed in parallel. Once all nested operations are finished, the parent operation is resumed.</li><li>The AsyncOperation<StatusType> can now also be used to return a result. Just return an OperationResult<StatusType> in the enumerator.</li><li>Fixed a bug with nested operations and exception handling<br /></li></ul></div><h2 xmlns="http://www.w3.org/1999/xhtml">Downloads</h2><table xmlns="http://www.w3.org/1999/xhtml"><tr><td><img style="border:none; vertical-align: middle;" src=file.axd?file=infinitec.threading_2_5/zip.gif /></td><td><a href=file.axd?file=infinitec.threading_2_5/InfiniTec.Threading.zip>InfiniTec.Threading.zip</a> (268,722 Bytes)<br />Version 2.5. Includes source, binaries and documentation</td></tr></table>2007-01-26T23:00:00+00:00hkrauseThis is a major upgrade for my multithreading library. If you are not familiar about what this library does, please read the other articles in this section. New in this release are parallel operations: You can return ParallelOperation from within an asynchronous operation. The execution of the current operation will be suspended until all nested operations are executed. Additionally, a maximum number of parallel executions can be passed along, which allows you to further control the execution of the nested operations. And of course, you can select which exceptions you want to catch on each of the nested operation.I'm primarily using these parallel operations in my InfiniTec.Exchange library to speed up lookups to Active Directory and the Exchange store. For example, when looking up members of a distribution list, I get a bunch of references, either to user in Active Directory, or references to contacts on an Exchange folder. Take a look at this method: 1 privateIEnumerable<OperationAction> RefreshInternal(AsyncOperation baseOperation) 2 { 3 Dictionary<NestedOperation,EntryIdReference> operations; 4 ExchangeStoreReference entryId; 5 List<IAddressListEntry> result; 6 7 _UnresolvableEntries = 0; 8 9 yieldreturnnewNestedOperation(baseOperation, false); 10 11 12 if (!_ResolveMembers) 13 { 14 _ResolveMembers = true; 15 yieldbreak; 16 } 17 18 entryId = newExchangeStoreReference(Properties.GetProperty<byte[]>(WellknownProperties.Item.EntryId).Value); 19 20 operations = newDictionary<NestedOperation, EntryIdReference>(); 21 22 foreach (EntryIdReference reference in DecodeMembers(entryId.StoreId)) 23 { 24 operations.Add( 25 newNestedOperation(reference.GetResolveOperation(BaseItem.Connection), false), 26 reference); 27 } 28 29 yieldreturnnewParallelOperations(operations.Keys, 5); 30 31 result = newList<IAddressListEntry>(); 32 33 foreach (KeyValuePair<NestedOperation, EntryIdReference> entry in operations) 34 { 35 AsyncOperation<IAddressListEntry> operation; 36 37 operation = (AsyncOperation<IAddressListEntry>) entry.Key.Operation; 38 if (operation.StatusInformation != null) 39 { 40 result.Add(operation.StatusInformation); 41 } 42 else 43 { 44 _UnresolvableEntries++; 45 } 46 } 47 48 _Members = result.AsReadOnly(); 49 }In Line 9, I call the original refresh method, using the NestedOperation operation action. After that, I decode those member entries in lines 22 to 27 and put them in a dictionary. The interesting thing happens in line 29. At this point, I return a ParallelOperation operation action with a list of those member references. Additionally, a maximum number of concurrent operations of 5 is specified. When the method resumes it's operation in line 31, all nested operations have been completed.http://www.infinitec.de/pingback.axdhttp://www.infinitec.de/post.aspx?id=6e1da7e5-94d4-4016-976a-b85dff8b6e9c0http://www.infinitec.de/trackback.axd?id=6e1da7e5-94d4-4016-976a-b85dff8b6e9chttp://www.infinitec.de/post/2007/01/27/Extreme-threading.aspx#commenthttp://www.infinitec.de/syndication.axd?post=6e1da7e5-94d4-4016-976a-b85dff8b6e9chttp://www.infinitec.de/post/2006/03/30/InfiniTecThreading-library.aspxInfiniTec.Threading library2006-03-29T22:00:00+00:00hkrause<h2 xmlns="http://www.w3.org/1999/xhtml">Description</h2><div xmlns="http://www.w3.org/1999/xhtml"><p>In his <a href="http://blogs.msdn.com/michen/archive/2006/03/30/564671.aspx">blog</a> Michael Entin talks about using C# iterators to simplify writing of asynchronous code. Be sure to read his article before proceeding!</p><p>If you want to do a web request using the <a href="http://msdn2.microsoft.com/en-us/library/system.net.webrequest(VS.80).aspx">WebRequest</a> class, you can either do this synchronously, like this one:</p><div class="clsCode" style="FONT-SIZE: 10pt; BACKGROUND-COLOR: #f0f0f0"><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 1</span> <span style="color: #0000ff;">public</span><span style="color: #0000ff;">string</span> DoRequest(<span style="color: #0000ff;">string</span> uri)</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 2</span> {</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 3</span> <span style="color: #008080;">WebRequest</span> request = <span style="color: #008080;">WebRequest</span>.Create(uri);</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 4</span> </span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 5</span> <span style="color: #008080;">WebResponse</span> response = request.GetResponse();</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 6</span> </span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 7</span> <span style="color: #0000ff;">using</span> (<span style="color: #008080;">Stream</span> stream = response.GetResponseStream())</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 8</span> {</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 9</span> <span style="color: #0000ff;">byte</span>[] buffer = <span style="color: #0000ff;">new</span><span style="color: #0000ff;">byte</span>[4096];</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 10</span> <span style="color: #008080;">StringBuilder</span> result = <span style="color: #0000ff;">new</span><span style="color: #008080;">StringBuilder</span>();</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 11</span> <span style="color: #0000ff;">do</span></span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 12</span> {</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 13</span> stream.Read(buffer, 0, buffer.Length);</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 14</span> </span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 15</span> <span style="color: #0000ff;">if</span> (bytesRead > 0)</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 16</span> {</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 17</span> result.Append(<span style="color: #008080;">Encoding</span>.UTF8.GetString(buffer, 0, bytesRead));</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 18</span> }</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 19</span> } <span style="color: #0000ff;">while</span> (bytesRead != 0);</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 20</span> </span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 21</span> <span style="color: #0000ff;">return</span> result.ToString();</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 22</span> }</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 23</span> }</span></p></div><p>Very nice and elegant. But a waste of resources. The operation blocks the caller (and with it probably the UI). The solution to this is to perform the entire request asynchronously: Use <a href="http://msdn2.microsoft.com/en-us/library/system.net.webrequest.begingetrequeststream(VS.80).aspx">BeginGetRequestStream</a>, <a href="http://msdn2.microsoft.com/en-us/library/system.net.webrequest.endgetrequeststream(VS.80).aspx">EndGetRequestStream</a>, and the other BeginXXX and EndXXX methods. You will end up with many callback methods for each of them. At the end you'll have a very complicated set of methods, which will be hard to debug.</p><p>Michael (and other) had the ingenious idea to use C# iterators to merge these two concepts, and take the best of both worlds together. The AsyncOperation class in this library implements his approach, at least the basic principles.</p></div><h2 xmlns="http://www.w3.org/1999/xhtml">Creative misuse…</h2><div xmlns="http://www.w3.org/1999/xhtml"><p>The basic idea is to implement the operation (e.g. downloading a website) as an iterator method:</p><div class="clsCode" style="FONT-SIZE: 10pt; BACKGROUND-COLOR: #f0f0f0"><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 1</span> <span style="color: #0000ff;">private</span><span style="color: #008080;">IEnumerable</span><<span style="color: #008080;">OperationAction</span>> DownloadUrlInternal(<span style="color: #0000ff;">string</span> url)</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 2</span> {</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 3</span> <span style="color: #008080;">WebRequest</span> request = <span style="color: #008080;">WebRequest</span>.Create(Url);</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 4</span> </span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 5</span> <span style="color: #008080;">IAsyncResult</span> ar = request.BeginGetResponse(<span style="color: #0000ff;">null</span>, <span style="color: #0000ff;">null</span>);</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 6</span> </span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 7</span> <span style="color: #008080;">ProgressUpdate</span><<span style="color: #008080;">Status</span>> update = <span style="color: #0000ff;">new</span><span style="color: #008080;">ProgressUpdate</span><<span style="color: #008080;">Status</span>>(<span style="color: #008080;">Status</span>.ReceivingResponse, ar.AsyncWaitHandle);</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 8</span> <span style="color: #0000ff;">yield</span><span style="color: #0000ff;">return</span> update;</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 9</span> </span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 10</span> <span style="color: #008080;">WebResponse</span> response = request.EndGetResponse(ar);</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 11</span> }</span></p></div><p>This method implements an iterator and returns an operation action of some sort. After a call to BeginGetResponse, the method creates a ProgressChangedEventStateBag which contains the status of the current request, as well as the Waithandle returned by the BeginGetResponse. Using the 'yield return' statement, this information is passed to the caller.</p><p>The method is not resumed until the Waithandle is signaled.</p><p>The DowndloadUrl method is not called directly by an application. Instead, a wrapper class is used, called AsyncOperation. This class ensures that the DownloadUrl method is called properly and translates those OperationActions into events like ProgressChanged, Completed, Error and Timedout.</p><p>The wrapper is written this way:</p><div class="clsCode" style="FONT-SIZE: 10pt; BACKGROUND-COLOR: #f0f0f0"><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 1</span> <span style="color: #0000ff;">public</span><span style="color: #008080;">AsyncOperation</span><<span style="color: #008080;">Status</span>, <span style="color: #0000ff;">string</span>> DownloadUrl(<span style="color: #0000ff;">string</span> url)</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 2</span> {</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 3</span> <span style="color: #0000ff;">return</span><span style="color: #0000ff;">new</span><span style="color: #008080;">AsyncOperation</span><<span style="color: #008080;">Status</span>, <span style="color: #0000ff;">string</span>>(DownloadUrlInternal(url, 3000);</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 4</span> }</span></p></div><p>This call initializes a new AsyncOperation class with a timeout of 3 seconds. The calling application can use this code to run the code:</p><div class="clsCode" style="FONT-SIZE: 10pt; BACKGROUND-COLOR: #f0f0f0"><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 1</span> helper = reader.DownloadUrl(tbUrl.Text);</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 2</span> </span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 3</span> helper.ProgressChanged += helper_ProgressChanged;</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 4</span> helper.Finished += helper_Finished;</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 5</span> helper.Error += helper_Error;</span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 6</span> </span></p><p style="PADDING-RIGHT: 0px; MARGIN-TOP: 0px; PADDING-LEFT: 2cm; MARGIN-BOTTOM: 0px; TEXT-INDENT: -2cm"><span style="font-family: Courier New;"><span style="color: #2b91af;"> 7</span> helper.Run();</span></p></div><p>The entire source code, as well as a sample application can be downloaded from the bottom of this page.</p><p>The AsyncOperation are intensively used in my <a href="software/nettoolbox/infinitec.threading.aspx">InfiniTec.Exchange</a> project.</p></div><h2 xmlns="http://www.w3.org/1999/xhtml">More Information</h2><div xmlns="http://www.w3.org/1999/xhtml"><p> </p><h3>The AsyncOperation classes</h3><div><p align="left">The main classes are the AsyncOperation and those derived from AsyncOperation. The most important members are displayed in this diagram:</p><p align="left"><a href=file.axd?file=infinitec.threading/60247db7a0fd49ce.jpg style="margin-right: 10px; display: block;"><img src=file.axd?file=infinitec.threading/preview_60247db7a0fd49ce.jpg float="false" style=";border: none;" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:xd="http://schemas.microsoft.com/office/infopath/2003" /><br /><span style="font-size: 8pt;font-style: italic;text-align: right;clear: left;"> (click to enlarge)</span></a></p><p align="left">Most of the logic is implemented in the AsyncOperation class. However, only the AsyncOperation<StatusType> and the AsyncOperation<StatusType, ResultType> classes can be used. The first one is best suited for operation which do not yield a result. For example copying an input stream to an output stream. An example for this is the AsyncOperations.Copy </p><p align="left"><div class="margin-right: 10px; display: block; width:600px;"><img src=file.axd?file=infinitec.threading/f0778b908bf24d6d.jpg float="false" style=";border: none;" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:xd="http://schemas.microsoft.com/office/infopath/2003" /><br /><span style="font-size: 8pt;font-style: italic;text-align: right;clear: left;"></span></div></p><p align="left">operation, which does exactly this. During progress updates, it returns the current operation (reading from the input stream or writing to the output stream), as well as the current progress, if the source stream supports seeking.</p><p align="left"> </p><p align="left">The second operation type reports status updates and returns a result. A good example for this would be the download of a file using the HttpWebRequest operation.</p><h4>The operation actions</h4><div><p>An asynchronous operation can return several different operation actions. These are displayed below:</p><p><a href=file.axd?file=infinitec.threading/241072e821854441.jpg style="margin-right: 10px; display: block;"><img src=file.axd?file=infinitec.threading/preview_241072e821854441.jpg float="false" style=";border: none;" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:xd="http://schemas.microsoft.com/office/infopath/2003" /><br /><span style="font-size: 8pt;font-style: italic;text-align: right;clear: left;"> (click to enlarge)</span></a></p><ul style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px" type="disc"><li>The ProgrssUpdate<StatusType> action is used to report progress changes to the caller. This is also the action that drives the operation. Every BeginXXX method should be followed by a ProgressUpdate action containing the IAsyncResult.AsyncWaitHandle.</li><li>The abstract ProgressUpdate action is only used internally.</li><li>The OperationResult<ResultType> is used to report the result of the operation.</li><li>The NestedOperation can be used to run an asynchronous operation from another asynchronous operation. The current operation is not resumed until the nested operation has completed. The calling operation can specifiy wether progress updates are propagated to the root operation. Addionally, the calling operation can specify, wehter it wants to handle a possible timeout of the nested operation and certain exception. Unhandled exceptions and timeouts are propagated up the call chain until a handler is found or until the root operation is reached.</li><li>The NestedOperation<StatusType> is a combination of the NestedOperation action and the ProgressUpdate<StatusType> action. This type will first report the progress update and then execute the nested operation.</li></ul></div><h4>Progress updates of nested operations</h4><div>The main problem of propagating the progress of nested operations to the root operation is the possible incompatible types of the status types. This is solved by marking properties of the status classes of the parent operation as a nested state. The NestedState attribute is used for this purpose. During a nested progress update, the status class of the parent operation is searched for a matching property which is marked with the NestedState attribute. </div></div></div><h2 xmlns="http://www.w3.org/1999/xhtml">ChangeLog</h2><div xmlns="http://www.w3.org/1999/xhtml"><h3>Version 2.0 (05/15/2006)</h3><div><p>This version is a major update… somewhere in the middle I lost track of the changes…. Some of the changes are:</p><ul style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px" type="disc"><li>New class AsyncOperations. Contains a basic Stream-to-Stream copy operation</li><li>Changed the name of the AsyncOperationBase class to AsyncOperation</li><li>Added an Update method to the ProgressUpdate class</li><li>The type of the Timeout property has been changed from int to TimeSpan</li><li>The StatusBag classes have benn renamed to OperationActions.</li><li>Nested operation are now better supported</li><li>Progress updates can now be reported from a nested operation to the root operation.</li><li>New Class AsyncOperation<StatusType> for operations which don't return a result</li><li>WaitForComletion operation added to the AsyncOperation class. Blocks the calling thread until the operation is finished.</li></ul></div><h3>Version 1.1 (03/31/2006)</h3><div><ul style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px" type="disc"><li> <span style="FONT-SIZE: 11pt"><span style="font-family: Calibri;">Added a WaitHandle that is signaled, when the status of the operation changes. This allows better nesting of asynchronous operations.</span></span></li><li style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px" valign="middle"><span style="FONT-SIZE: 11pt"><span style="font-family: Calibri;">Added Status, StatusInformation, Result and Exception properties. Now the current statatus can be read from the AsyncOperation class.</span></span></li><li style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px" valign="middle"><span style="FONT-SIZE: 11pt"><span style="font-family: Calibri;">Added Abort method to the AsyncOperation class.</span></span></li><li style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px" valign="middle"><span style="FONT-SIZE: 11pt"><span style="font-family: Calibri;">OnFinished in OnCompleted umbenannt</span></span></li><li style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px" valign="middle"><span style="FONT-SIZE: 11pt"><span style="font-family: Calibri;">Added NDoc documentation</span></span></li></ul></div><h3>Version 1.0 (03/30/2006)</h3><div><ul style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px" type="disc"><li>Initial release</li></ul></div></div><h2 xmlns="http://www.w3.org/1999/xhtml">Downloads</h2><table xmlns="http://www.w3.org/1999/xhtml"><tr><td><img style="border:none; vertical-align: middle;" src=file.axd?file=infinitec.threading/zip.gif /></td><td><a href=file.axd?file=infinitec.threading/InfiniTec.Threading%20Release.zip>InfiniTec.Threading Release.zip</a> (28,518 Bytes)<br />Binaries version 2.1 with debug symbols.. Signed with the InfiniTec private key.</td></tr><tr><td><img style="border:none; vertical-align: middle;" src=file.axd?file=infinitec.threading/zip.gif /></td><td><a href=file.axd?file=infinitec.threading/InfiniTec.Threading%20source.zip>InfiniTec.Threading source.zip</a> (21,563 Bytes)<br />Source code for the InfiniTec.Threading assembly.</td></tr><tr><td><img style="border:none; vertical-align: middle;" src=file.axd?file=infinitec.threading/zip.gif /></td><td><a href=file.axd?file=infinitec.threading/InfiniTec.Threading%20help.zip>InfiniTec.Threading help.zip</a> (129,542 Bytes)<br />Help file containing the documentation for the assembly.</td></tr></table>2006-03-29T22:00:00+00:00hkrauseThis library contains a helper class which allows the development of asynchronous methods using a pseudo-synchronous style.http://www.infinitec.de/pingback.axdhttp://www.infinitec.de/post.aspx?id=4745ba40-8fd4-4037-82f8-78f92b5f2f490http://www.infinitec.de/trackback.axd?id=4745ba40-8fd4-4037-82f8-78f92b5f2f49http://www.infinitec.de/post/2006/03/30/InfiniTecThreading-library.aspx#commenthttp://www.infinitec.de/syndication.axd?post=4745ba40-8fd4-4037-82f8-78f92b5f2f49