惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Google DeepMind News
Google DeepMind News
F
Fortinet All Blogs
量子位
G
Google Developers Blog
J
Java Code Geeks
N
Netflix TechBlog - Medium
博客园 - 聂微东
宝玉的分享
宝玉的分享
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
月光博客
月光博客
The Cloudflare Blog
Apple Machine Learning Research
Apple Machine Learning Research
爱范儿
爱范儿
雷峰网
雷峰网
M
MIT News - Artificial intelligence
T
Tailwind CSS Blog
V
Visual Studio Blog
阮一峰的网络日志
阮一峰的网络日志
博客园 - 三生石上(FineUI控件)
Microsoft Azure Blog
Microsoft Azure Blog
aimingoo的专栏
aimingoo的专栏
Martin Fowler
Martin Fowler
有赞技术团队
有赞技术团队
T
The Blog of Author Tim Ferriss

The Old New Thing

Magic statics vs. std::call_once - The Old New Thing Why do Microsoft job levels start in the high 50's instead of starting at a sane number like 1? - The Old New Thing Why didn't Read­Directory­ChangesW provide a way to correlate the two sides of a rename operation? - The Old New Thing How can I remove the Close button from my window caption? - The Old New Thing Why is the x86 undefined instruction called ud2? Why 2? - The Old New Thing What algorithm did Windows XP use to choose your initial user picture? - The Old New Thing A sample use of the winstart.bat file in Windows 95 - The Old New Thing Why don't we allow stacks to be sparse, instead of forcing them to be contiguous? - The Old New Thing What happens if you change a window class's GCL_CB­WND­EXTRA? - The Old New Thing The case of the progress callback that never got called when progress happened - The Old New Thing The perils of binding to value types in XAML - The Old New Thing Microspeak: Funded / unfunded - The Old New Thing AWE does not require PAE, though PAE makes it much more useful - The Old New Thing On forcing all derived classes to implement a specific non-virtual method, part 2 - The Old New Thing On forcing all derived classes to implement a specific non-virtual method, part 1 - The Old New Thing In the product end game, every change carries significant risk, episode 2 - The Old New Thing Why didn't the Windows Entertainment Pack just run the MS-DOS version inside an emulator? - The Old New Thing Comparing the two holograms on the Windows 95 box - The Old New Thing Reducing C++ template bloat by factoring out the type-dependent portions of the function, practical exam - The Old New Thing Reducing C++ template bloat by factoring out the type-dependent portions of the function - The Old New Thing On wrapping a callable in a lambda that just calls it with the same parameters - The Old New Thing Why did the Microsoft Entertainment Pack for Windows have a special sticker announcing that it also had Tetris? - The Old New Thing How do functions like alloca allocate memory from the stack? - The Old New Thing How do functions like alloca allocate memory from the stack? - The Old New Thing Forcing an ARM64X executable to run as a specific architecture - The Old New Thing A little helper class for managing LPPROC_THREAD_ATTRIBUTE_LISTs - The Old New Thing The comments that go into code versus those that go into the pull request description - The Old New Thing The little-known winstart.bat batch file - The Old New Thing How can I perform a Copy­File&shy in unbuffered mode? - The Old New Thing Creating a fake agile wrapper that is technically agile but is not useful outside its home apartment, part 5 - The Old New Thing
std::call_once vs. std::async - The Old New Thing
Raymond Chen · 2026-09-17 · via The Old New Thing

Last time, we compared magic statics with std::call_once and concluded that std::call_once lets you construct magic statics-like behavior for non-static variables.

But there is also std::async for delayed execution. Can we use that instead?

The idea here is that you tell std::async that you want it to defer execution of something (say, a lambda). It returns a std::future representing that deferred execution.

auto f = std::async(std::launch::deferred, ⟦ lambda ⟧);

At some later point, you can ask for the deferred execution to execute and retrieve the result.

auto value = future.get();

There are a few catches here.

To permit getting non-copyable types, getting the value is a destructive operation: You are allowed to call get() 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 std::future to a std::shared_future:

auto f = std::async(std::launch::deferred, ⟦ lambda ⟧).share();

When you call get() on a shared_future, it gives you a const reference to the cached value and retains the cached value for future calls. The shared_future::get() method is marked const, which in the C++ standard library means that it is thread-safe with respect to itself and other const members. Therefore, you can call get() 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.

Okay, so our Gadget class can look like this:

class Gadget
{
public:
    Gadget(std::shared_ptr<Widget> const& widget) : widget(widget) {}

    bool can_reverse_polarity()
    {
        return can_reverse_polarity_future.get();
    }

private:
    std::shared_ptr<Widget> const widget;
    std::shared_future<bool> const can_reverse_polarity_future =
        std::async(std::launch::deferred,
            [=] {
                return is_configuration_enabled("polarity_reversal") &&
                is_widget_polarity_reversible(*widget);
            }).share();
};

So why choose one over the other?

Well, std::call_once is very small. Visual Studio builds it out of the Win32 INIT_ONCE, which is the size of a pointer.¹

On the other hand std::future and std::shared_future involve a heap allocation to manage the shared state, as well to store the invocable and its parameters, and the result. Also, since std::async 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 std::launch::async, so it links in the thread library, as well as other machinery to support wait_for.)

But a significant difference between them has to do with their exception behavior, which we haven’t even talked about yet.

We’ll do that next time.

¹ I can’t find what gcc builds it out of, but an old implementation I found just builds it manually 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 moved around, but it’s still intact. It seems that the lack of exception safety is called out with a todo-like comment. 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’t prevent the compiler from reordering the stores and loads. The compiler might decide to inline the callback and then reorder the stores so that the store to done happens before the end of the callback.

Category

Topics

Author

Raymond Chen

Raymond has been involved in the evolution of Windows for more than 30 years. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie-jeebies. The Web site spawned a book, coincidentally also titled The Old New Thing (Addison Wesley 2007). He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information.