C++ async & launch | "Asynchronous Execution" Guide

C++ async & launch | "Asynchronous Execution" Guide

이 글의 핵심

std::async and std::launch policies: async vs deferred, thread creation, and when futures block—C++11 concurrency patterns with std::future.

What is std::async?

std::async is a C++11 API that executes a function asynchronously and retrieves the result using a future. The result can also be shared using promise·future and shared_future. If you’re familiar with thread basics, it’s easy to use.

#include <future>

auto future = std::async([]() {
    return 42;
});

int result = future.get();  // Wait for the result

launch Policies

Comparison of launch Policies

PolicyExecution TimingThread CreationUse Case
launch::asyncImmediately✅ New threadCPU-intensive tasks
launch::deferredOn get() call❌ Current threadConditional execution
async | deferredImplementation-definedAuto-selectedGeneral use
// async: New thread
auto f1 = std::async(std::launch::async, func);

// deferred: Delayed execution
auto f2 = std::async(std::launch::deferred, func);

// Default: async | deferred
auto f3 = std::async(func);

Execution Flow by Policy

sequenceDiagram
    participant Main as Main Thread
    participant Async as Async Thread

    Note over Main: launch::async
    Main->>Async: start immediately
    Main->>Main: other work
    Async->>Async: background exec
    Main->>Async: future.get()
    Async->>Main: return result

    Note over Main: launch::deferred
    Main->>Main: async call (no exec)
    Main->>Main: other work
    Main->>Main: future.get()
    Main->>Main: exec func now
    Main->>Main: return result

Practical Examples

Example 1: Basic Usage

#include <future>
#include <iostream>

int compute(int x) {
    std::this_thread::sleep_for(std::chrono::seconds(1));
    return x * x;
}

int main() {
    auto future = std::async(compute, 10);
    
    std::cout << "Computing..." << std::endl;
    
    int result = future.get();  // Wait
    std::cout << "Result: " << result << std::endl;  // 100
}

Example 2: Multiple Asynchronous Tasks

auto f1 = std::async([]() { return compute1(); });
auto f2 = std::async([]() { return compute2(); });
auto f3 = std::async([]() { return compute3(); });

// Parallel execution
int r1 = f1.get();
int r2 = f2.get();
int r3 = f3.get();

int total = r1 + r2 + r3;

Parallel Execution Timeline:

gantt
    title Parallel vs Sequential Execution Comparison
    dateFormat X
    axisFormat %L
    
    section Parallel (async)
    compute1 :0, 1000
    compute2 :0, 1000
    compute3 :0, 1000
    
    section Sequential Execution
    compute1 :0, 1000
    compute2 :1000, 2000
    compute3 :2000, 3000

Performance Comparison:

Execution ModeTask DurationTotal TimePerformance Gain
Sequential Execution1 sec × 33 sec-
Parallel Execution (async)1 sec (concurrent)1 sec3x

Example 3: launch Policies

// Immediate execution (new thread)
auto f1 = std::async(std::launch::async, []() {
    std::cout << "Asynchronous execution" << std::endl;
    return 42;
});

// Delayed execution (on get call)
auto f2 = std::async(std::launch::deferred, []() {
    std::cout << "Deferred execution" << std::endl;
    return 42;
});

f2.get();  // Executes here

Example 4: Exception Handling

auto future = std::async([]() {
    throw std::runtime_error("Error");
    return 42;
});

try {
    int result = future.get();  // Rethrows exception
} catch (const std::exception& e) {
    std::cout << "Exception: " << e.what() << std::endl;
}

Checking future Status

auto future = std::async(std::launch::async, compute);

// Wait
future.wait();

// Timeout
auto status = future.wait_for(std::chrono::seconds(1));

if (status == std::future_status::ready) {
    std::cout << "Completed" << std::endl;
} else if (status == std::future_status::timeout) {
    std::cout << "Timeout" << std::endl;
}

Common Issues

Issue 1: Future Destruction

// ❌ Ignoring future
std::async([]() {
    // Task
});  // Blocks in destructor

// ✅ Store the future
auto future = std::async([]() {
    // Task
});

Issue 2: Multiple get Calls

auto future = std::async([]() { return 42; });

int r1 = future.get();  // OK
// int r2 = future.get();  // Error: get already called

// get can only be called once

Issue 3: Exception Propagation

auto future = std::async([]() {
    throw std::runtime_error("Error");
});

// Exception is thrown on get call
try {
    future.get();
} catch (...) {
    // Handle exception
}

Issue 4: Deadlock

// ❌ Potential deadlock
std::mutex mtx;

auto future = std::async([&mtx]() {
    std::lock_guard<std::mutex> lock(mtx);
    // ...
});

std::lock_guard<std::mutex> lock(mtx);
future.get();  // Deadlock

async vs thread

// std::async: Simple, returns future
auto future = std::async([]() { return 42; });
int result = future.get();

// std::thread: Manual management
std::thread t([]() {
    // Hard to return result
});
t.join();

FAQ

Q1: When to use std::async?

A: When you need to execute a function asynchronously and retrieve its result.

Q2: What are the launch policies?

A:

  • async: New thread
  • deferred: Delayed execution

Q3: get vs wait?

A:

  • get: Returns the result
  • wait: Only waits for completion

Q4: How to handle exceptions?

A: Exceptions are rethrown when get is called.

Q5: Performance considerations?

A: Thread creation has overhead. For small tasks, this overhead might outweigh the benefits.

Q6: Resources for learning async?

A:

  • “C++ Concurrency in Action”
  • “Effective Modern C++”
  • cppreference.com

Related Posts: future and promise, shared_future, Thread Basics, packaged_task.