{"id":611,"date":"2013-12-23T13:00:22","date_gmt":"2013-12-23T13:00:22","guid":{"rendered":"http:\/\/writeasync.net\/?p=611"},"modified":"2013-12-14T17:40:52","modified_gmt":"2013-12-14T17:40:52","slug":"async-fixed-concurrency-workflows","status":"publish","type":"post","link":"http:\/\/writeasync.net\/?p=611","title":{"rendered":"Async fixed concurrency workflows"},"content":{"rendered":"<p>In the <a href=\"http:\/\/writeasync.net\/?p=551\" title=\"Designing for fixed concurrency\">previous post<\/a>, I mentioned async fixed concurrency workflows as providing the best balance between resource utilization, latency, and throughput. Let&#8217;s explore two ways to build such a workflow.<\/p>\n<h2>Single-threaded workflow<\/h2>\n<p>The first design we will look at involves a single-threaded workflow. The idea is that we want many calls in flight at a given moment but we only want to schedule and handle completion on a single thread. The advantage is that we don&#8217;t have to do any locking while preparing each call, although this means we will never launch more than one call at a time.<\/p>\n<p>We&#8217;ll start with an <code>OperationManager<\/code> class and the minimal interface:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\npublic class OperationManager\r\n{\r\n    public OperationManager(int maxPendingCalls, Func&lt;Task&gt; doAsync)\r\n    {\r\n    }\r\n\r\n    public Task RunAsync(int totalCalls)\r\n    {\r\n        throw new NotImplementedException();\r\n    }\r\n}\r\n<\/pre>\n<p>After a few basic unit tests (<a href=\"https:\/\/github.com\/brian-dot-net\/writeasync\/commit\/cd125b419ddc99dff04abb4347247d3a1e1a1482\">Max of one allows only one call at a time<\/a>, <a href=\"https:\/\/github.com\/brian-dot-net\/writeasync\/commit\/2481bde7589eac8ecde83ece8a5789b14da6b100\">Max of one with call count 3 allows only one call at a time for 3 iterations<\/a>), we get to the point where we need to handle multiple calls in flight concurrently. How will we proceed?<\/p>\n<p>The basic idea is that we want to use a <strong>semaphore<\/strong> to limit the concurrency level to our max pending call count. Before launching the call, we should wait for the semaphore. After the call has completed, it should release the semaphore. Of course, we need an <strong>async<\/strong> semaphore in order to avoid blocking. <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.threading.semaphoreslim(v=vs.110).aspx\">SemaphoreSlim<\/a> happens to have <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.threading.semaphoreslim.waitasync(v=vs.110).aspx\">WaitAsync<\/a> methods, but in this implementation I chose to go with Stephen Toub&#8217;s <a href=\"http:\/\/blogs.msdn.com\/b\/pfxteam\/archive\/2012\/02\/12\/10266983.aspx\">AsyncSemaphore<\/a> instead; it is more async TDD-friendly since it runs continuations synchronously instead of on the thread pool like <code>SemaphoreSlim<\/code>.<\/p>\n<h2>Multi-threaded workflow<\/h2>\n<p>Implementing a multi-threaded workflow is much simpler. In this case, you only need to launch a series of async loops, up to the max pending call count. Calls will be launched and completed concurrently in this case, so be aware of any shared state that you are manipulating and add proper locking where necessary.<\/p>\n<p>To see how all this code evolved, you can get the full commit history here: <a href=\"https:\/\/github.com\/brian-dot-net\/writeasync\/commits\/master\/projects\/ConcurrentSample\">History for writeasync\/projects\/ConcurrentSample<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>In the previous post, I mentioned async fixed concurrency workflows as providing the best balance between resource utilization, latency, and throughput. Let&#8217;s explore two ways to build such a workflow. Single-threaded workflow The first design we will look at involves&hellip; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[21,61,41],"tags":[],"class_list":["post-611","post","type-post","status-publish","format-standard","hentry","category-async","category-concurrency","category-tdd"],"_links":{"self":[{"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/posts\/611","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/writeasync.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=611"}],"version-history":[{"count":0,"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/posts\/611\/revisions"}],"wp:attachment":[{"href":"http:\/\/writeasync.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=611"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/writeasync.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=611"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/writeasync.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=611"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}