C++ 11中的异步构造函数

Cri*_*ano 20 c++ constructor asynchronous c++11

有时我需要创建构造函数需要很长时间才能执行的对象.这导致UI应用程序中的响应性问题.

所以我想知道编写一个被设计为异步调用的构造函数是否合理,方法是将回调函数传递给它,这会在对象可用时提醒我.

以下是示例代码:

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;
}
Run Code Online (Sandbox Code Playgroud)

你觉得这个设计有什么缺点吗?或者你知道是否有更好的方法吗?

编辑

按照JoergB的回答提示,我试着写一个工厂,负责以同步或异步方式创建一个对象:

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));
    }
};
Run Code Online (Sandbox Code Playgroud)

Joe*_*rgB 17

你的设计似乎非常具有侵入性.我没有看到为什么课程必须知道回调的原因.

就像是:

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

或者干脆

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

或者只是(没有未来但是回调参与争议):

async(launchopt, [&callback]() {
      unique_ptr<C> ptr(new C());
      callback(ptr);
})
Run Code Online (Sandbox Code Playgroud)

应该做的一样,不应该吗?这些还确保仅在构造完整对象时(从C派生时)调用回调.

将这些中的任何一个变成通用的async_construct模板应该不会太费力.


Dan*_*nas 9

封装你的问题.不要考虑异步构造函数,只考虑封装对象创建的异步方法.

  • 第二个.如果你创建一个对象,那么正常的期望是它在构造函数返回时完全构造 - 而不是其他东西.构造函数抛出,或者您有一个有效的对象,没有猜测.另一方面,创建构造对象的异步任务没有任何问题(从它的角度来看是同步的). (4认同)
  • @Cristiano这是一个选项.我实际上只是说我不喜欢将异步性绑定到本机构造函数本身. (2认同)