Stdcall Once In C++ - C++ Programming Tutorial
C++ Course / Advanced Topics / Stdcall Once In C++

Stdcall Once In C++

BLUF: Mastering Stdcall Once In C++ is a critical step in becoming a proficient C++ developer. This lesson provides a deep dive into the syntax, performance considerations, and real-world applications of this concept.
Key Performance Insight: Stdcall Once In C++

C++ is renowned for its efficiency. Learn how Stdcall Once In C++ enables low-level control and high-performance computing in the tutorial below.

In C++, the std::callonce function guarantees the execution of a specified function only once, even in the presence of multiple simultaneous calls from different threads. When a thread invokes std::callonce with a specific flag and function, it first checks if any other thread is already executing the same operation. If there is no ongoing execution, the thread proceeds to execute the function fn with the provided arguments args. However, if another thread is already in the process of executing std::call_once with the same flag, the current thread will enter a passive state, waiting for the ongoing execution to finish.

After the active execution finishes, regardless of its outcome, the passive thread exits without running 'fn'.

Crucially, all observable outcomes of live execution are coordinated among all simultaneous invocations bearing the identical indicator. Should a live invocation terminate with an error and there exist dormant executions, one of the dormant executions is selected to take over as the new live invocation. Upon the conclusion of a live execution, all existing dormant executions and upcoming invocations with the same indicator promptly return without transitioning into live executions. This guarantees that the operation is executed exactly once, upholding thread safety and synchronization in scenarios involving multiple threads.

Syntax:

It has the following syntax:

Example

void std::call_once(std::once_flag& flag, Callable&& fn, Args&&... args);

Parameters:

  • Flag: The 'fllag' is a std::once_flag object. This flag is used to coordinate the execution of a given function across many threads.
  • Fn: The 'fn' denotes a callable object (such as a function pointer, function object, or lambda expression) that should be executed once.
  • Args: The 'args' are the arguments that will be passed to the function fn.
  • Return Value:

    Exceptions:

  • If the function or callable passed to std::callonce throws an exception, the exception will propagate to the thread that invoked std::callonce.
  • However, the std::onceflag is not considered initialized if an exception occurs. Subsequent calls to std::callonce with the same std::once_flag will attempt the function again.
  • Key points:

  • If many calls to std::callonce pass different functions 'f', it is unclear which 'f' will be called. The provided function executes on the same thread as the std::callonce invocation that it was passed.
  • Even when called from multiple threads, function-local statics are guaranteed to be initialized once, and they may be more efficient than the equivalent code using std::call_once.
  • This method is comparable to pthread_once on POSIX systems.
  • Example:

Let's consider a scenario to demonstrate the std::call_once function in C++.

Example

#include <iostream>
#include <thread>
#include <mutex>
#include <vector>
#include <chrono>
// Global variables
std::once_flag init_flag;
int shared_resource = 0;
// Initialization function
void initialize_resource()
{
    std::cout << "Initializing shared resource by thread: " 
              << std::this_thread::get_id() << std::endl;
    // Simulate a time-consuming operation
    std::this_thread::sleep_for(std::chrono::seconds(2));
    shared_resource = 42; // Resource is initialized
    std::cout << "Shared resource initialized to " << shared_resource 
              << " by thread: " << std::this_thread::get_id() << std::endl;
}
// Function executed by multiple threads
void access_shared_resource() 
{
    // Ensure the initialization function is executed only once
    std::call_once(init_flag, initialize_resource);
    // Access the shared resource
    std::cout << "Thread " << std::this_thread::get_id() 
              << " is using the shared resource: " << shared_resource << std::endl;
}
int main() 
{
    // Create multiple threads to access the shared resource
    const int thread_count = 5;
    std::vector<std::thread> threads;
    for (int i = 0; i < thread_count; ++i) 
    {
        threads.emplace_back(access_shared_resource);
    }
    // Wait for all threads to complete
    for (auto& t : threads) 
    {
        t.join();
    }
    std::cout << "All threads have completed their work." << std::endl;
    return 0;
}

Output:

Output

Initializing shared resource by thread: 124622399161920
Shared resource initialized to 42 by thread: 124622399161920
Thread 124622399161920 is using the shared resource: 42
Thread 124622390769216 is using the shared resource: 42
Thread 124622248158784 is using the shared resource: 42
Thread 124622382376512 is using the shared resource: 42
Thread 124622301296192 is using the shared resource: 42
All threads have completed their work.

Explanation:

This program utilizes the std::callonce method to guarantee that a common resource is initialized only once, even in scenarios where multiple threads are accessing it. The initializeresource function is responsible for setting up the shared resource (sharedresource) with a delay simulation, and its activation is managed by the std::once flag. Each thread spawned within the main function invokes accesssharedresource, leveraging std::callonce to ensure that the initialization process is carried out just once, irrespective of the number of active threads. Subsequent threads that interact with the resource can directly utilize the initialized value without having to repeat the initialization step. This approach ensures thread safety, single-time initialization, and illustrates the effectiveness of concurrent programming.

Input Required

This code uses input(). Please provide values below:

Logic Practice
Install Logic Practice
Add to home screen for a faster app-like experience