Tuesday 17 January 2017

Step by step multithreading : Chapter 9, Future And Promise

So far We have learnt how to create threads, synchronize it, and make a thread safe programs in CPP. We have also seen how to pass or move object from parent thread to child thread, now let’s learn how we can get data from child thread to parent thread.

First a small recap of how we pass data from parent thread to child thread, through a small factorial program.
#include<iostream>
#include<future>
#include<thread>
using namespace std;

void factorial(int number);

int main()
{
    thread t1(factorial, 4);
    t1.join();
    return 0;
}

void factorial(int number)
{
    int result = 1;
    for(int index = number ; index >= 1; index--)
    {
        result *= index;
    }
    cout << "factorial of number - " <<number << " is - " <<result;
}


Now the question is, what to do, if I want value back from my child thread. One easy way to do this, is to share the memory between two threads. So if we pass any object through reference we can access it from both the threads.
// solving the problem mentioned in Concept01.cpp
#include<iostream>
#include<future>
#include<thread>
using namespace std;

void factorial(int number);

int main()
{
    int getResult;
    thread t1(factorial, 4, std::ref(getResult) );
    t1.join();
    return 0;
}

void factorial(int number, int& getResult)
{
    int result = 1;
    for(int index = number ; index >= 1; index--)
    {
        result *= index;
    }
    getResult = result;
}

Above mentioned program will work well enough, but have one issue. As we have shared the object between two threads, now we have to make sure, it is thread safe, for that we have to use mutex. Also we have to make sure, first child thread fill the value, then only parent thread should access it, yes you guess it right, to do that we need conditional variables. Ohhh, all this will make my code complex, indeed it will.

My aim is simple get value from child thread, for that I have to right so much code? Can’t I do this with some simple steps? 

Yes, of course we can do it in simple way, through <future>. First look at the below mentioned program (Please read the comment section).
#include<iostream>
#include<future>
#include<thread>
using namespace std;

int factorial(int number);

int main()
{
    int getResult;
    std::future<int> futureObject = std::async(factorial, 4);
    /*
    future class represent the future. where you can get an object. from future.
    
    async function may or may not create any thread, that will also depend upon the extra parameter 
    which we can pass to async function.
    
    std::future<int> futureObject = std::async(std::launch::deferred, factorial, 4);
        this means async function will not create any thread. it will deferred the execution of 
        factorial function until futureObject.get() get called. So when futureObject.get() 
        called it will then get executed in the same thread.
    
    std::future<int> futureObject = std::async(std::launch::async, factorial, 4);
        This will create another thread.
    
    std::future<int> futureObject = std::async(std::launch::async | std::launch::deferred, factorial, 4);
        Here we have added both the parameter, which means whether it will create a thread or not 
        will be get determined by the implementation.
        
    The defult value of function async is -
        std::launch::async | std::launch::deferred 
        
    */
    getResult = futureObject.get(); // this function will wait till the child thread finished. and then it 
                          // will return the value to getResult, which has came from child thread.
                          // we can call get() method only once. if again we call futureObject.get() it may 
                          // crashed the program.
    return 0;
}

int factorial(int number)
{
    int result = 1;
    for(int index = number ; index >= 1; index--)
    {
        result *= index;
    }
    cout << "factorial of number - " <<number << " is - " <<result;
    return result;
}

So through future, we have passed value, from child thread to parent thread. But we can also pass the value from parent thread to child thread, may be not at the time of creation, but in future.

Now compare this program with the version where we have used mutex and conditional variables. this way of dealing it, with situation where you want to share the data between threads, are much better, of course mutex and conditional variables are important, but for this specific case, we can use future, which will reduce code complexities.

We have seen a very high level of what future does and how it helps us, now let us understand the promiseFirst look at the below mentioned program (Please read the comment section).
// Passing value from child to parent
#include<iostream>
#include<future>
#include<thread>
#include<chrono>
using namespace std;

int factorial(std::future<int>& f);

int main()
{
    int getResult;
    std::promise<int> p;
    std::future<int> f = p.get_future();
    std::future<int> fu = std::async(std::launch::async, factorial, std::ref(f));
    // do some work, we can add many lines of code, though we have initiated call to factorial
    // without passing the value, but there could be a chance that we dont have value at this 
    // point of time.
    std::this_thread::sleep_for(chrono::milliseconds(20));
    // now set the value, for child thread to use.
    p.set_value(4); // after this call, child thread will get the value - 4
    getResult = fu.get(); // now get value from child thread.
    cout << "factorial of value 4 , from child - " <<getResult ;
    return 0;
}

int factorial(std::future<int>& f )
{
    int result = 1;
    int number = f.get(); // wait here to get the value.
    for(int index = number ; index >= 1; index--)
    {
        result *= index;
    }
    return result;
}
/*
--------------------------------------------------------------OP
$ ./a.exe
factorial of value 4 , from child - 24
*/

Lets analyze the program, I have called a function, in this example factorial, for which I don't have any value to pass at that moment. So I have passed another future object, that will wait till the promise gives value to it.

The vary first question comes in our mind, that what if we forget to pass value, through promise, p.set_value(4)? Then at later stage, it will get exception from int number = f.get(); and exception type will be std::future_errc::broken_promise exception.

This is very powerful, feature. We can also set customized exception, like mentioned below.
p.set_exception(std::make_exception_ptr(std::runtime_error("Sorry I am not passing the value")))

So you will get meaningful error, in this case - "Sorry I am not passing the value". 

Future and promise cannot be copied, like thread and uniq_lock objects, it can only be moved.
std::promise<int> p;
std::promise<int> p2 = p; // this will give compilation error.
// i can use move
std::promise<int> p2 = std::move(p);

Now last but not the least, what if I want to create many child threads? Then how to manage the code, because I cannot pass same future object to all different threads, as each future object can call thread function only once. 

One solution of this (which is not a good one), create N future objects and N promise objects, so we will have one promise per future object pair, and each pair will call desire function once, see below mentioned code snippet.
int main()
{
    int getResult;
    std::promise<int> p01;
    std::promise<int> p02;
    // .. till 10 promise 
    std::future<int> f01 = p01.get_future();
    std::future<int> f02 = p02.get_future();
    // .. till 10 future 
    std::future<int> fu = std::async(std::launch::async, factorial, std::ref(f01));
    std::future<int> fu2 = std::async(std::launch::async, factorial, std::ref(f02));
    // .. till 10 threads 
    /*
        So 1 promise and 1 future pair per thread. but that is messy, again can we have something 
        else?
    */
    // do some work, apply some business logic.
    std::this_thread::sleep_for(chrono::milliseconds(20));
    // now set the value, for child thread to use.
    p.set_value(4); // after this child thread will get the value - 4
    getResult = fu.get();
    cout << "factorial of value , got from child - " <<getResult ;
    return 0;
}

As mentioned in the code comment, this will be very messy and lengthy approach. So do we have any other way of handling it? Yes we do have.

We can solve above mentioned problem through shared_future, this can be shared with N number of threads. It is very nice when we have broadcast kind of communication model.

int main()
{
    int getResult;
    std::promise<int> p;
    std::future<int> f = p.get_future();
    std::shared_future<int> sf = f.share();
    std::future<int> fu = std::async(std::launch::async, factorial, sf);
    std::future<int> fu2 = std::async(std::launch::async, factorial, sf);
    std::future<int> fu3 = std::async(std::launch::async, factorial, sf);
    // .. till 10 threads 
    std::this_thread::sleep_for(chrono::milliseconds(20));
    p.set_value(4); 
    // now when we set value 4, all the child thread (10 threads) will get value 4.
    getResult = fu.get();
    cout << "factorial of value , got from child - " <<getResult ;
    return 0;
}

In this case we will pass any one value, through only one promise object, to all threads, through shared_thread object. 

Thanks for reading this, please write questions or some more useful information related to this topic on comment section. To learn more about threading, see the full learning index here, or join multithreading learning page on FB or on G++.

No comments:

Post a Comment