{"id":56018,"date":"2012-02-12T08:44:38","date_gmt":"2012-02-12T08:44:38","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/pfxteam\/2012\/02\/12\/building-async-coordination-primitives-part-6-asynclock\/"},"modified":"2012-02-12T08:44:38","modified_gmt":"2012-02-12T08:44:38","slug":"building-async-coordination-primitives-part-6-asynclock","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/building-async-coordination-primitives-part-6-asynclock\/","title":{"rendered":"Building Async Coordination Primitives, Part 6: AsyncLock"},"content":{"rendered":"<p>Last time, we looked at building an <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/building-async-coordination-primitives-part-5-asyncsemaphore\/\">AsyncSemaphore<\/a>.\u00a0 Here, we\u2019ll look at building support for an async mutual exclusion mechanism that supports scoping via \u2018using\u2019.<\/p>\n<p>As mentioned in the previous post, semaphores are great for throttling and resource management.\u00a0 You can give a semaphore an initial count of the number of things to protect, and then it\u2019ll only allow that many consumers to successfully acquire the semaphore, forcing all others to wait until a resource is freed up and count on the semaphore is released.\u00a0 That resource to protect could be the right to enter a particular region of code, and the count could be set to 1: in this way, you can use a semaphore to achieve mutual exclusion, e.g.<\/p>\n<blockquote><p><span style=\"font-family: Consolas;font-size: small\"><span style=\"color: #0000ff\">private readonly<\/span> <span style=\"color: #4bacc6\">AsyncSemaphore<\/span> m_lock = <span style=\"color: #0000ff\">new<\/span> <span style=\"color: #4bacc6\">AsyncSemaphore<\/span>(1);\n\u2026\n<span style=\"color: #0000ff\">await<\/span> m_lock.WaitAsync();\n<span style=\"color: #0000ff\">try<\/span>\n{\n\u2026 <span style=\"color: #008000\">\/\/ protected code here<\/span>\n}\n<span style=\"color: #0000ff\">finally<\/span> { m_lock.Release(); }<\/span><\/p><\/blockquote>\n<p>We could simplify this slightly by creating an AsyncLock type that supports interaction with the \u2018using\u2019 keyword.\u00a0 Our goal is to be able to achieve the same thing as in the previous code snippet but instead via code like the following:<\/p>\n<blockquote><p><span style=\"font-family: Consolas;font-size: small\"><span style=\"color: #0000ff\">private readonly<\/span> <span style=\"color: #4bacc6\">AsyncLock<\/span> m_lock = <span style=\"color: #0000ff\">new<\/span> <span style=\"color: #4bacc6\">AsyncLock<\/span>();\n\u2026\n<span style=\"color: #0000ff\">using<\/span>(<span style=\"color: #0000ff\">var <span style=\"color: #000000\">releaser<\/span><\/span><span style=\"color: #000000\"> = <span style=\"color: #0000ff\">await<\/span> m_lock<\/span>.LockAsync())\n{\n\u2026 <span style=\"color: #008000\">\/\/ protected code here<\/span>\n}<\/span><\/p><\/blockquote>\n<p>To achieve this, we\u2019ll build the following type:<\/p>\n<blockquote><p><span style=\"font-family: Consolas\"><span style=\"font-size: small\"><span style=\"color: #0000ff\">public class<\/span> <span style=\"color: #4bacc6\">AsyncLock<\/span>\n{\n<span style=\"color: #0000ff\">public<\/span> AsyncLock();<\/span><\/span><\/p>\n<p><span style=\"font-family: Consolas;font-size: small\">\u00a0\u00a0\u00a0 <span style=\"color: #0000ff\">public<\/span> <span style=\"color: #4bacc6\">Task<\/span>&lt;<span style=\"color: #4bacc6\">Releaser<\/span>&gt; LockAsync();<\/span><\/p>\n<p><span style=\"font-family: Consolas;font-size: small\">\u00a0\u00a0\u00a0 <span style=\"color: #0000ff\">public struct<\/span> <span style=\"color: #4bacc6\">Releaser<\/span> : <span style=\"color: #4bacc6\">IDisposable<\/span>\n{\n<span style=\"color: #0000ff\">public void<\/span> Dispose();\n}\n}<\/span><\/p><\/blockquote>\n<p>Internally, we\u2019ll maintain two members.\u00a0 We\u2019ll use an AsyncSemaphore to handle the bulk of the logic.\u00a0 We\u2019ll also cache a Task&lt;Releaser&gt; instance to use when accesses to the lock are uncontended and thus we can avoid unnecessary allocations.<\/p>\n<blockquote><p><span style=\"font-family: Consolas;font-size: small\"><span style=\"color: #0000ff\">private readonly<\/span> <span style=\"color: #4bacc6\">AsyncSemaphore<\/span> m_semaphore;\n<span style=\"color: #0000ff\">private readonly<\/span> <span style=\"color: #4bacc6\">Task<\/span>&lt;<span style=\"color: #4bacc6\">Releaser<\/span>&gt; m_releaser;<\/span><\/p><\/blockquote>\n<p>The Releaser is just an IDisposable implementation with a Dispose method that will call Release on the underlying semaphore.\u00a0 This is what allows us to use the construct with \u2018using\u2019, such that the finally block generated by the \u2018using\u2019 will call Release on the semaphore just as we did in our hand-written example.<\/p>\n<blockquote><p><span style=\"font-family: Consolas;font-size: small\"><span style=\"color: #0000ff\">public struct<\/span> <span style=\"color: #4bacc6\">Releaser<\/span> : IDisposable\n{\n<span style=\"color: #0000ff\">private readonly<\/span> <span style=\"color: #4bacc6\">AsyncLock<\/span> m_toRelease;<\/p>\n<p><span style=\"color: #0000ff\">internal<\/span> Releaser(<span style=\"color: #4bacc6\">AsyncLock<\/span> toRelease) { m_toRelease = toRelease; }<\/p>\n<p><span style=\"color: #0000ff\">public void<\/span> Dispose()\n{\n<span style=\"color: #0000ff\">if<\/span> (m_toRelease != <span style=\"color: #0000ff\">null<\/span>)\nm_toRelease.m_semaphore.Release();\n}\n}<\/span><\/p><\/blockquote>\n<p>Our AsyncLock\u2019s constructor will just initialize the members, creating a semaphore with an initial count of 1, and creating the cached releaser task with a releaser that points to this AsyncLock instance:<\/p>\n<blockquote><p><span style=\"font-family: Consolas;font-size: small\"><span style=\"color: #0000ff\">public<\/span> AsyncLock()\n{\nm_semaphore = <span style=\"color: #0000ff\">new<\/span> <span style=\"color: #4bacc6\">AsyncSemaphore<\/span>(1);\nm_releaser = <span style=\"color: #4bacc6\">Task<\/span>.FromResult(<span style=\"color: #0000ff\">new<\/span> <span style=\"color: #4bacc6\">Releaser<\/span>(<span style=\"color: #0000ff\">this<\/span>));\n}<\/span><\/p><\/blockquote>\n<p>And, finally, we need our Lock method.\u00a0 We first call WaitAsync on the semaphore to get back a Task that represents our acquisition of the lock.\u00a0 If the task is already completed, then we can synchronously return our cached Task&lt;Releaser&gt;; again, this means that if the lock is uncontended, there are no allocations.\u00a0 If the wait task is not yet completed, then we return a continuation Task&lt;Releaser&gt; that will complete and hand back a new Releaser when the wait completes.<\/p>\n<blockquote><p><span style=\"font-family: Consolas;font-size: small\"><span style=\"color: #0000ff\">public<\/span> <span style=\"color: #4bacc6\">Task<\/span>&lt;<span style=\"color: #4bacc6\">Releaser<\/span>&gt; LockAsync()\n{\n<span style=\"color: #0000ff\">var<\/span> wait = m_semaphore.WaitAsync();\n<span style=\"color: #0000ff\">return<\/span> wait.IsCompleted ?\nm_releaser :\nwait.ContinueWith((_,state) =&gt; <span style=\"color: #0000ff\">new<\/span> Releaser((<span style=\"color: #4bacc6\">AsyncLock<\/span>)state),\n<span style=\"color: #0000ff\">this<\/span>, <span style=\"color: #4bacc6\">CancellationToken<\/span>.None,\n<span style=\"color: #4bacc6\">TaskContinuationOptions<\/span>.ExecuteSynchronously, <span style=\"color: #4bacc6\">TaskScheduler<\/span>.Default);\n}<\/span><\/p><\/blockquote>\n<p>Next time, we\u2019ll step things up a bit and try our hand at implementing an <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/building-async-coordination-primitives-part-7-asyncreaderwriterlock\/\">asynchronous reader\/writer lock<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Last time, we looked at building an AsyncSemaphore.\u00a0 Here, we\u2019ll look at building support for an async mutual exclusion mechanism that supports scoping via \u2018using\u2019. As mentioned in the previous post, semaphores are great for throttling and resource management.\u00a0 You can give a semaphore an initial count of the number of things to protect, and [&hellip;]<\/p>\n","protected":false},"author":360,"featured_media":58792,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[7908],"tags":[7925,36,7916,7909,7912],"class_list":["post-56018","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-pfxteam","tag-net-4-5","tag-async","tag-coordination-data-structures","tag-parallel-extensions","tag-task-parallel-library"],"acf":[],"blog_post_summary":"<p>Last time, we looked at building an AsyncSemaphore.\u00a0 Here, we\u2019ll look at building support for an async mutual exclusion mechanism that supports scoping via \u2018using\u2019. As mentioned in the previous post, semaphores are great for throttling and resource management.\u00a0 You can give a semaphore an initial count of the number of things to protect, and [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/56018","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/360"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=56018"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/56018\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/58792"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=56018"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=56018"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=56018"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}