Thỉnh thoảng tôi cần phải tạo các đối tượng có các nhà xây dựng mất nhiều thời gian để thực thi. Điều này dẫn đến các vấn đề về khả năng đáp ứng trong các ứng dụng UI. Vì vậy, tôi đã tự hỏi nếu nó có thể là hợp lý để viết một nhà xây dựng được thiết kế để được gọi là không đồng bộ, bằng cách đi qua một cuộc gọi lại với nó mà sẽ cảnh báo cho tôi khi đối tượng có sẵn.Nhà xây dựng không đồng bộ trong C++ 11
Dưới đây là một số mẫu mã:
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,¬ified,&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;
}
Bạn có thấy bất kỳ nhược điểm trong thiết kế này? Hoặc bạn có biết nếu có một cách tiếp cận tốt hơn?
EDIT
Tiếp theo gợi ý của câu trả lời JoergB, tôi đã cố gắng để viết một nhà máy này sẽ chịu trách nhiệm để tạo ra một đối tượng trong một sync hoặc async cách:
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));
}
};
bạn đã thử dùng std :: async bên trong hàm tạo. tôi tưởng tượng bạn có thể chỉ cần đặt async vào callback và lưu trữ kết quả là một thành viên của chính lớp đó. – thang
@thang Tôi muốn thử nó ... vấn đề với tôi là bạn có nguy cơ có một đối tượng được tạo nhưng chưa sẵn sàng sử dụng. Phương thức isValid() có thể giúp ích trong trường hợp này, có lẽ ... – Cristiano
vâng bạn có thể thêm isValid hoặc waitValid hoặc một cái gì đó vào hiệu ứng đó. theo cách đó mọi thứ được đóng gói vào trong lớp ... cùng chức năng, chỉ cần một chút neater. – thang