设计一个结合同步和异步操作的c++ API
Design for a C++ API combining synchronous and asynchronous operations
我正在设计一个暴露同步和异步操作的c++ API。所有操作都可能失败,必须报告失败。异步操作必须提供一种在完成时执行延续的方法。我正在努力以最易读和一致的方式设计API。
这是一个例子,说明我现在的设计:
#include <memory>
#include <future>
using namespace std;
class Error {
public:
Error(int c, string desc) : code(c), description(desc) {}
int code;
string description;
};
template<typename T>
class Callback {
public:
virtual void completed(const T& result, unique_ptr<Error> error) = 0;
};
template<typename T>
class PrintCallback : public Callback<T> {
public:
void completed(const T& result, unique_ptr<Error> error) override {
if (nullptr != error) {
printf("An error has occured. Code: %d Description: %sn",
error->code, error->description.c_str());
} else {
printf("Operation completed successfully. Result: %sn",
to_string(result).c_str());
}
}
};
class API {
public:
void asyncOperation(shared_ptr<Callback<int>> callback) {
thread([callback]() {
callback->completed(5, nullptr);
}).detach();
}
int syncOperation(unique_ptr<Error>& error) {
return 5;
}
void asyncFailedOperation(shared_ptr<Callback<int>> callback) {
thread([callback]() {
callback->completed(-1, unique_ptr<Error>(new Error(222, "Async Error")));
}).detach();
}
int syncFailedOperation(unique_ptr<Error>& error) {
error = unique_ptr<Error>(new Error(111, "Sync Error"));
return -1;
}
};
我不喜欢在同步操作中使用error out参数,以及同步和异步签名之间的不一致。我正在讨论两种选择:
- 将同步操作视为异步操作,并让它们接受回调以返回结果/失败。这种方法在同步和异步操作之间更加一致,并且看起来更干净。另一方面,让一个简单的同步操作与Callback一起工作感觉有点奇怪。
- 使用
std::promise
和std::future
,并使用异常报告故障。对于异步操作,将返回std::future
,并在失败的情况下抛出其get()
。同步操作将在失败的情况下抛出。这种方法感觉更干净,因为错误处理不会干扰方法签名,异常是c++中进行错误处理的标准方法。然而,要获得结果,我必须调用future::get()
,所以如果我不想阻塞,那么我必须启动另一个线程来等待结果。异步操作的延续运行在实际在std::promise
上设置结果的线程上,这一点也很重要。这种方法是对这个问题的公认答案——同步和异步api。
我想知道:
- 如果选项#2中的额外线程可以避免。
- 如果替代方案#2的缺点大于优点(特别是额外的线程)。
- 如果有其他方法我没有考虑过。
- 哪种方法被认为是可读性和一致性的最佳选择。
谢谢!
- Q:是否可以避免选项#2中的额外线程
A:避免额外线程的一种方法是将线程池与任务队列一起使用。这种方法仍然使用额外的线程,但是线程的数量是固定的,而不是与创建的任务数量成比例。
问:如果替代方案#2的缺点大于优点(特别是额外的线程)
A:我不这么认为。
:是的。在我看来,最好的方法是让一切都异步,为boost::asio提供类似的API,利用boost::asio::io_service和lambda函数,并实现线程池和任务队列。您可以将sync作为async的包装器实现如下:进行async调用并等待std::condition_variable。async调用的回调信号是condition_variable.
问:哪种方法被认为是可读性和一致性的最佳选择
答:异步代码不会像同步代码那样可读。如果可读性对你来说真的很重要,那就放弃异步。另一方面,如果你想要异步和一致性,就像我上面概述的那样,让一切都是异步的。
除此之外,在我看来你不应该实现你自己的Error类。看看std::error_code(以及error_condition、error_category)。一篇好文章是http://blog.think-async.com。已经有人帮你弄明白了。
相关文章:
- 无法在 WinRT 中获取异步操作结果 (Windows::Foundation::IAsyncOperating 接口
- 异步操作的 Asio 处理程序在其同步对应项正常工作时不会调用
- 我不明白异步操作如何使HTTP服务器并发
- 提升::Asio 使用异步操作时传输的字节
- 如何编写异步操作?
- 发生哪些线程异步操作
- C 11异步操作在树上的操作
- 在 boost::asio 中启动异步操作和运行io_service的正确顺序
- 如何实现多线程异步操作
- FTP异步操作[Wininet C++]上的ERROR_IO_PENDING
- 使用带有boost::asio异步操作的自定义streambuf
- 提升 ASIO,如何取消异步操作
- 提升异步操作不起作用(对我来说)
- 在同一条链中执行异步操作
- 将异步操作的处理程序绑定到另一个类的非静态成员
- 如果一个basic_waitable_timer在仍有异步操作等待的情况下被销毁,该怎么办
- FILE_FLAG_IO_BUFFERING会减慢同步读取操作的速度
- boost::asio中异步操作的处理程序要求
- 设计一个结合同步和异步操作的c++ API
- 提升asio同步与异步操作性能