{"id":112706,"date":"2026-09-17T07:00:00","date_gmt":"2026-09-17T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=112706"},"modified":"2026-09-17T10:46:12","modified_gmt":"2026-09-17T17:46:12","slug":"20260917-00-2","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20260917-00\/?p=112706","title":{"rendered":"<CODE>std::call_once<\/CODE> vs. <CODE>std::async<\/CODE>"},"content":{"rendered":"<p>Last time, we compared <a title=\"Magic statics vs. std::call_once\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20260916-00\/?p=112703\"> magic statics with <code>std::call_once<\/code><\/a> and concluded that <code>std::call_once<\/code> lets you construct magic statics-like behavior for non-static variables.<\/p>\n<p>But there is also <code>std::async<\/code> for delayed execution. Can we use that instead?<\/p>\n<p>The idea here is that you tell <code>std::async<\/code> that you want it to defer execution of something (say, a lambda). It returns a <code>std::future<\/code> representing that deferred execution.<\/p>\n<pre>auto f = std::async(std::launch::deferred, \u27e6 lambda \u27e7);\r\n<\/pre>\n<p>At some later point, you can ask for the deferred execution to execute and retrieve the result.<\/p>\n<pre>auto value = future.get();\r\n<\/pre>\n<p>There are a few catches here.<\/p>\n<p>To permit getting non-copyable types, getting the value is a destructive operation: You are allowed to call <code>get()<\/code> only once, and subsequent calls result in undefined behavior. This is a problem for the case where you ask for the value multiple times, but you can fix it by converting the <code>std::<wbr \/>future<\/code> to a <code>std::<wbr \/>shared_<wbr \/>future<\/code>:<\/p>\n<pre>auto f = std::async(std::launch::deferred, \u27e6 lambda \u27e7)<span style=\"border: solid 1px currentcolor;\">.share()<\/span>;\r\n<\/pre>\n<p>When you call <code>get()<\/code> on a <code>shared_<wbr \/>future<\/code>, it gives you a const reference to the cached value and retains the cached value for future calls. The <code>shared_<wbr \/>future::<wbr \/>get()<\/code> method is marked <code>const<\/code>, which in the C++ standard library means that it is thread-safe with respect to itself and other <code>const<\/code> members. Therefore, you can call <code>get()<\/code> as many times as you like, and the first will run the lambda and return the result, and the others will return the already-calculated result.<\/p>\n<p>Okay, so our <code>Gadget<\/code> class can look like this:<\/p>\n<pre>class Gadget\r\n{\r\npublic:\r\n    Gadget(std::shared_ptr&lt;Widget&gt; const&amp; widget) : widget(widget) {}\r\n\r\n    bool can_reverse_polarity()\r\n    {\r\n        return can_reverse_polarity_future.get();\r\n    }\r\n\r\nprivate:\r\n    std::shared_ptr&lt;Widget&gt; const widget;\r\n    std::shared_future&lt;bool&gt; const can_reverse_polarity_future =\r\n        std::async(std::launch::deferred,\r\n            [=] {\r\n                return is_configuration_enabled(\"polarity_reversal\") &amp;&amp;\r\n                is_widget_polarity_reversible(*widget);\r\n            }).share();\r\n};\r\n<\/pre>\n<p>So why choose one over the other?<\/p>\n<p>Well, <code>std::call_<wbr \/>once<\/code> is very small. Visual Studio builds it out of the Win32 <code>INIT_ONCE<\/code>, which is the size of a pointer.\u00b9<\/p>\n<p>On the other hand <code>std::<wbr \/>future<\/code> and <code>std::<wbr \/>shared_<wbr \/>future<\/code> involve a heap allocation to manage the shared state, as well to store the invocable and its parameters, and the result. Also, since <code>std::<wbr \/>async<\/code> supports other modes of execution, you pull in code to support those other modes that you might even be using. (For example, it has to worry about the possibility that you pass <code>std::<wbr \/>launch::<wbr \/>async<\/code>, so it links in the thread library, as well as other machinery to support <code>wait_<wbr \/>for<\/code>.)<\/p>\n<p>But a significant difference between them has to do with their exception behavior, which we haven&#8217;t even talked about yet.<\/p>\n<p>We&#8217;ll do that next time.<\/p>\n<p>\u00b9 I can&#8217;t find what gcc builds it out of, but an old implementation I found just <a href=\"https:\/\/classpages.cselabs.umn.edu\/current\/csci5103\/tools\/installs\/gcc-4.8.3+os161-2.1\/libgcc\/config\/i386\/gthr-win32.c\"> builds it manually<\/a> with many defects. Just a quick look at it shows that it is not exception-safe and suffers from data races. The code appears to have <a href=\"https:\/\/github.com\/gcc-mirror\/gcc\/blob\/9149a5b7e0a66b7b94d5b7db3194a975d18dea2f\/libgcc\/config\/i386\/gthr-win32.h#L629\"> moved around<\/a>, but it&#8217;s still intact. It seems that <a href=\"https:\/\/github.com\/gcc-mirror\/gcc\/blob\/9f6eb7d2993046e5720275f590098db0ac0188d4\/libstdc%2B%2B-v3\/include\/std\/mutex#L922\"> the lack of exception safety is called out with a todo-like comment<\/a>. The data race is addressed by a comment saying that the processor implicitly makes all loads acquire and all stores release, and while that may be true, it doesn&#8217;t prevent the <i>compiler<\/i> from reordering the stores and loads. The compiler might decide to inline the callback and then reorder the stores so that the store to <code>done<\/code> happens before the end of the callback.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Simple vs complex machinery.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-112706","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Simple vs complex machinery.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112706","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=112706"}],"version-history":[{"count":1,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112706\/revisions"}],"predecessor-version":[{"id":112707,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112706\/revisions\/112707"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=112706"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=112706"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=112706"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}