Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Async constructor in C++11

Tags:

Sometimes I need to create objects whose constructors take very long time to execute. This leads to responsiveness problems in UI applications.

So I was wondering if it could be sensible to write a constructor designed to be called asynchronously, by passing a callback to it which will alert me when the object is available.

Below is a sample code:

class C
{
public:
    // Standard ctor
    C()
    {
        init();
    }

    // Designed for async ctor
    C(std::function<void(void)> callback)
    {
        init();
        callback();
    }

private:
    void init() // Should be replaced by delegating costructor (not yet supported by my compiler)
    {
        std::chrono::seconds s(2);
        std::this_thread::sleep_for(s);
        std::cout << "Object created" << std::endl;
    }
};

int main(int argc, char* argv[])
{
    auto msgQueue = std::queue<char>();
    std::mutex m;
    std::condition_variable cv;
    auto notified = false;

    // Some parallel task
    auto f = []()
    {
        return 42;
    };

    // Callback to be called when the ctor ends
    auto callback = [&m,&cv,&notified,&msgQueue]()
    {
        std::cout << "The object you were waiting for is now available" << std::endl;
        // Notify that the ctor has ended
        std::unique_lock<std::mutex> _(m);
        msgQueue.push('x');
        notified = true;
        cv.notify_one();
    };

    // Start first task
    auto ans = std::async(std::launch::async, f);

    // Start second task (ctor)
    std::async(std::launch::async, [&callback](){ auto c = C(callback); });

    std::cout << "The answer is " << ans.get() << std::endl;

    // Mimic typical UI message queue
    auto done = false;
    while(!done)
    {
        std::unique_lock<std::mutex> lock(m);
        while(!notified)
        {
            cv.wait(lock);
        }
        while(!msgQueue.empty())
        {
            auto msg = msgQueue.front();
            msgQueue.pop();

            if(msg == 'x')
            {
                done = true;
            }
        }
    }

    std::cout << "Press a key to exit..." << std::endl;
    getchar();

    return 0;
}

Do you see any drawback in this design? Or do you know if there is a better approach?

EDIT

Following the hints of JoergB's answer, I tried to write a factory which will bear the responsibility to create an object in a sync or async way:

template <typename T, typename... Args>
class FutureFactory
{
public:
    typedef std::unique_ptr<T> pT;
    typedef std::future<pT> future_pT;
    typedef std::function<void(pT)> callback_pT;

public:
    static pT create_sync(Args... params)
    {
        return pT(new T(params...));
    }

    static future_pT create_async_byFuture(Args... params)
    {
        return std::async(std::launch::async, &FutureFactory<T, Args...>::create_sync, params...);
    }

    static void create_async_byCallback(callback_pT cb, Args... params)
    {
        std::async(std::launch::async, &FutureFactory<T, Args...>::manage_async_byCallback, cb, params...);
    }

private:
    FutureFactory(){}

    static void manage_async_byCallback(callback_pT cb, Args... params)
    {
        auto ptr = FutureFactory<T, Args...>::create_sync(params...);
        cb(std::move(ptr));
    }
};
like image 286
Cristiano Avatar asked Feb 04 '13 10:02

Cristiano


People also ask

Can I use async in constructor?

We should avoid delegating async work onto the constructor because it was never designed for that use case.

Can we make constructor async in C#?

Constructors cannot be async , but static methods can.

Can you have an async constructor in TypeScript?

To create an async constructor functions in TypeScript, we can create a factory method. to create the MyClass class that has a private constructor. We use it to create a MyClass instance inside the CreateAsync function. The function is static so we don't need to instantiate MyClass to call it.


2 Answers

Your design seems very intrusive. I don't see a reason why the class would have to be aware of the callback.

Something like:

future<unique_ptr<C>> constructedObject = async(launchopt, [&callback]() {
      unique_ptr<C> obj(new C());
      callback();
      return C;
})

or simply

future<unique_ptr<C>> constructedObject = async(launchopt, [&cv]() {
      unique_ptr<C> ptr(new C());
      cv.notify_all(); // or _one();
      return ptr;
})

or just (without a future but a callback taking an argument):

async(launchopt, [&callback]() {
      unique_ptr<C> ptr(new C());
      callback(ptr);
})

should do just as well, shouldn't it? These also make sure that the callback is only ever called when a complete object is constructed (when deriving from C).

It shouldn't be too much effort to make any of these into a generic async_construct template.

like image 90
JoergB Avatar answered Sep 20 '22 06:09

JoergB


Encapsulate your problem. Don't think about asynchronous constructors, just asynchronous methods which encapsulate your object creation.

like image 36
Daniel Daranas Avatar answered Sep 19 '22 06:09

Daniel Daranas