Friday, February 28, 2025

Execution Classes and functions

Overview
These classes can execute code asynchronously.

Details
async()
async is the win32 equivalent of ThreadPool discussed earlier. It's a function template defined as below.
NameDescription
  1. template <class Fn, class... Args>
    future<typename result_of<Fn(Args...)>::type> async
    (launch policy, Fn&& fn, Args&&... args)
  2. template <class Fn, class... Args>
    future<typename result_of<Fn(Args...)>::type> async
    (Fn&& fn, Args&&... args)
  1. The first constructor accepts function object for the callback and arguments. The launch policy launch::async  is implied.
  2. The second  constructor accepts launch policy,  launch::async  or launch::deferred, function object for the callback and arguments. 
Both returns a future object that contains the results or exceptions.

A caller can access the results from the future object by calling get() or wait() function
The launch policy decides how the callback is run. In case of launch::async, the callback function will be run immediately and asynchronously in a separate thread from possibly a thread pool.
In case of deferred,  the callback runs on the thread  that calls get() or wait() methods on the future object. In other words, running callback is deferred until get() or wait() methods are called. Also, wait_for() and wait_until() methods returns deferred value.

Example
void printresult(future<int>& result)
{
    try
    {
        cout << result.get() << endl;
    }
    catch(const exception& e)
    {
        cout << "exception caught:\t" << e.what() << endl;
    }    
}

void test(int n)
{
    future<int> result = async ([](int n)
    {
        if (n < 0)
           throw (runtime_error("no negative number"));
        else
            return (n*2);
    }, n);

    printresult(result);
} //prints 20 test(10); //prints:exception caught: no negative number test(-10);

This example 5  demonstrates functionality of the async object as in its console output. 

thread
thread object is the win32 equivalent of Thread discussed earlier. 

member types
NameDescription
native_handle_typeimplementation-defined
idrepresents the id of a thread

constructors
NameDescription
  1. thread() 
  2. template <class Fn, class... Args>
    thread (Fn&& fn, Args&&... args)
  3. thread (thread&& x)
  1. Default constructor.
  2. Construct a thread object that represents a new joinable thread of execution. The new thread of execution calls fn passing args as arguments (using decay copies of its lvalue or rvalue references).
  3. Move constructor.

methods
The following lists commonly used methods.
NameDescription
bool joinable()Checks whether the thread is joinable. A thread object is joinable if it represents a thread of execution.
id get_id()Returns the id of the thread.
native_handle_type native_handle()Returns the underlying implementation-defined thread handle
unsigned thread::hardware_concurrency()Returns the number of concurrent threads supported by the implementation
void join()Waits for the thread to finish its execution. This is applicable only if the thread is not detached.
void detach()Permits the thread to execute independently from the thread handle.

thread object can be used in two ways.
In the first method depicted below, the caller creates thread and wait tills the thread function returns. The behavior is similar to set_value_at_thread_exit(), the caller has to wait even if the result is set in the promise object earlier before thread function completion.
//main thread
promise<int> p;
//runs in different thread
thread {doubler,10, ref(p)}.join();
//retrive in main thread
auto result = p.get_future().get();


This method is most flexible, the get returns immediately after the return value is set in the promise object.
//main thread
promise<int> p;
//runs in different thread
thread {doubler,10, ref(p)}.detach();
//retrive in main thread
auto
result = p.get_future().get();
This  example 6  demonstrates various functionality of the thread class as in its console output.
In this example 7, the main  thread creates a thread object to print numbers serially as in 
its console output.  

Example
void printresult(future<int>& result)
{
    result.wait();
    try
    {
        cout << result.get() << endl;
    }
    catch(const exception& e)
    {
        cout << "exception caught:\t" << e.what() << endl;
    }    
}

void test(int n)
{
    promise<int> p;
    auto result = p.get_future();
    thread ([](int n,promise<int>& p)
    {
        if (n < 0)
            p.set_exception(make_exception_ptr(runtime_error("no negative number")));
        else
            p.set_value(n*2);
    }, n, ref(p)).detach();  
   
    printresult(result);
}   

    //prints 20
    test(10);
    
   //prints:exception caught:	no negative number
    test(-10);

packaged_task
template< class R, class ...ArgTypes >
class packaged_task<R(ArgTypes...)>
packaged_task object is similar to async except it can be launched at a later time like a lambda function using the function operator. 
By default, it runs on the same thread that calls it. However, to be asynchronous, it needs to be explicitly run from a separate thread.

constructors
NameDescription
  1. packaged_task() 
  2. template< class F >
    packaged_task( F&& f )
  3. packaged_task( packaged_task&& rhs )
  1. Default constructor.
  2. The object has a shared state, and its stored task is initialized to fn.
  3. Move constructor.

methods
The following lists commonly used methods.
NameDescription
future<R> get_future()Gets the future object.
bool valid()Checks the object has valid function.
void reset()Resets the state abandoning any stored results of previous executions, making it reusable.
void operator()(Args... args)
Calls the stored task, forwarding args as its arguments.

- If the call to the stored task completes successfully, the value it returns (if any) is stored in the shared state.
- If the call to the stored task throws an exception, the exception is caught and also stored in the shared state.

In both cases, the shared state is made ready (which unblocks any threads currently waiting for it).
void make_ready_at_thread_exit (Args... args)Same as operator() except the shared state is set ready at thread exit, instead of as soon as the call completes.
This example 8  demonstrates functionality of the packaged_task object as seen in its console output.   

Example
void printresult(future<int>& result)
{
    result.wait();
    try
    {
        cout << result.get() << endl;
    }
    catch(const exception& e)
    {
        cout << "exception caught:\t" << e.what() << endl;
    }    
}

void test(int n, bool t)
{
    auto ptask = packaged_task<int(int)>([](int n) 
    {
	if (n < 0)
            throw (runtime_error("no negative number"));
        else
            return (n*2);
    });
    
    auto result = ptask.get_future();
    if (!t)
        ptask(n);
    else
    {
        thread ([&ptask](int n)
        {
            ptask(n);
        },n).detach();
    }
    printresult(result);
}   


    //prints 20
    test(10,false);
    
   //prints:exception caught:	no negative number
    test(-10,true);

this_thread 
Provides functions that access the current thread.

NameDescription
thread::id this_thread::get_id() Returns thread id
void this_thread::yield()Yield to other threads
void this_thread::sleep_until(time_point tp)Sleep until time point 
void this_thread::sleep_for(duration d)Sleep for time span

This example 9  demonstrates the functionality of the this_thread apis as seen in its console output.

No comments:

Post a Comment