Wednesday 19 October 2016

Step by step multithreading : Functor

First understand what is functor? 
Any object who act like a function is called functor. The question here is how any object will act or look like function? Well if you have overload operator () in your class, you can give a look and feel of your object like a function, and we call it functor.

Till now we have seen, we pass function to thread object, but we can also pass functor to it - 
// we can also define functor
#include <iostream>
#include <thread>
using namespace std;

class PrintMe
{
    public:
        void operator()()
        {
            cout<<"Printing from thread, ";
        }
};

int main(int argc, char **argv)
{
    PrintMe functor1;
    thread t1(functor1);    // remember we use to pass function name here
                            // but we are passing an object, who looks like 
                            // function because of operator(). which make 
                            // it to print the message.
    t1.join();
    return 0;
}

/*
$ ./a.exe
Printing from thread,
*/

It is not necessary to create functor and pass it to thread, we can also call constructor while creating the thread object, like mentioned below -
// we can also define functor
#include <iostream>
#include <thread>
using namespace std;

class PrintMe
{
    public:
        void operator()()
        {
            cout<<"Printing from thread";
        }
};
int main(int argc, char **argv)
{
    thread t1((PrintMe())); // this will also work.
   t1.join();
    return 0;
}
/*
$ ./a.exe
Printing from thread
*/

Now the question is, can we pass any function parameter to functor, like we pass to functions? The answer is yes, we can pass parameter to functor, look at below code -
/*
even we can pass argument to functor.
*/
#include <iostream>
#include <thread>
using namespace std;

class PrintMe
{
    public:
        void operator()(string imput)
        {
            cout<<"Printing from thread"<<endl;
            cout<<"msg - "<< imput <<endl;
        }
};
int main(int argc, char **argv)
{
    string s1 = "Hello child thread ";
    thread t1((PrintMe()), s1);
    t1.join();
    return 0;
}
/*
$ ./a.exe
Printing from thread
msg - Hello child thread
*/

As C++ developer, I will think to pass my functor parameter by reference, to save extra works, like crating temp object, calling constructor and then destructor after it goes out of scope, etc. So I will write code where I will use pass by reference, like mentioned below -
/*
even we can pass argument to functor.
*/
#include <iostream>
#include <thread>
using namespace std;

class PrintMe
{
    public:
        void operator()(string& imput)
        {
            cout<<"Printing from thread"<<endl;
            cout<<"msg - "<< imput <<endl;
        }
};
int main(int argc, char **argv)
{
    string s1 = "Hello child thread ";
    thread t1((PrintMe()), s1);
    t1.join();
    return 0;
}

If I do that, I will get compilation error, as thread parameter cannot be passed as reference, you will get below mentioned compilation error -

functional:1505:61: error: no type named ‘type’ in ‘class std::result_of<PrintMe(std::basic_string<char>)>’

       typedef typename result_of<_Callable(_Args...)>::type result_type;

So the question is what we can do, if we want to pass parameter by reference. If we really willing to share the resource between parent thread and child thread, and want to pass it by reference, C++ standard library provide std::ref() function which will help to pass parameter by reference, look at below mentioned code for implementation -
/*
even we can pass argument to functor.
*/
#include <iostream>
#include <thread>
using namespace std;

class PrintMe
{
    public:
        void operator()(string& imput)
        {
            cout<<"Printing from thread"<<endl;
            cout<<"msg - "<< imput <<endl;
        }
};
int main(int argc, char **argv)
{
    string s1 = "Hello child thread ";
    thread t1((PrintMe()), std::ref(s1)); // this will work.
    t1.join();
    return 0;
}
/*
$ ./a.exe
Printing from thread
msg - Hello child thread
*/

We can check that, value is also being changed from child thread if you pass by reference, just to verify it, which is not possible through pass by value -
#include <iostream>
#include <thread>
#include <string>
using namespace std;

class PrintMe
{
    public:
        void operator()(string& imput)
        {
            cout<<"Printing from thread"<<endl;
            cout<<"msg - "<< imput <<endl;
            imput = "Modified the msg";
        }
};
int main(int argc, char **argv)
{
    string s1 = "Hello child thread ";
    thread t1((PrintMe()), std::ref(s1)); // this will also work.
    t1.join();
    
    cout<<"Printing it from main thread - "<<s1;
    return 0;
}
/*
$ ./a.exe
Printing from thread
msg - Hello child thread
Printing it from main thread - Modified the msg
*/

But sharing resources between threads are not always preferable, as there will be a chance that both threads can run and finish independently, without any dependency, and sometime it lead to memory related issues. In that case you just have to move resource from one thread to other thread, it is safe and efficient as well. After that, resource will belong to moved thread, C++ standard library provide std::move() function to do that, please have a look at below mentioned program -

/*
there are many things in C++ which can only be moved and not copied.

if we are not using any memory in main thread, we can move that 
memory to other thread who is looking for it, instead of sharing it.

because sharing will create data leak problems.
we can do pass by value, in that case, but that is not efficient, 
because then temp object creation/deletion will take place.

we can move the memory to other thread.
*/
#include <iostream>
#include <thread>
#include <string>
using namespace std;

class PrintMe
{
    public:
        void operator()(string imput)
        {
            cout<<"Printing from thread"<<endl;
            cout<<"msg - "<< imput <<endl;
            imput = "Modified the msg";
        }
};
int main(int argc, char **argv)
{
    string s1 = "Hello child thread ";
    thread t1((PrintMe()), std::move(s1)); // this will move thread from
                                           // main thread to child thread.
    t1.join();
    cout<<"Printing it from main thread - "<<s1;
    return 0;
}
/*
$ ./a.exe
Printing from thread
msg - Hello child thread
Printing it from main thread -
*/

Apart from moving the object from parent thread to child thread, we have to use std::move() when we are copying two threads. Actually we cannot copy the thread objects, it will give you compilation error. We can only move the thread objects from one object to another object, look at the below mentioned program, how we implement it - 

/*
thread cont be copied it can only be moved.
*/
#include <iostream>
#include <thread>
#include <string>
using namespace std;

class PrintMe
{
    public:
        void operator()(string imput)
        {
            cout<<"Printing from thread"<<endl;
            cout<<"msg - "<< imput <<endl;
            imput = "Modyfied the msg";
        }
};

int main(int argc, char **argv)
{
    string s1 = "Hello child thread ";
    thread t1((PrintMe()), std::move(s1)); 
    //thread t2 = t2            // this line of code will give me compilation error.
                                // but I can move the thread from one thread to another thread

    thread t2 = std::move(t1);  // we have to use move. 
                                // now t1 will be empty 

    t2.join();                  // call join in t2 now
    return 0;
}

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