关于如何将CompletionQueue
用于异步C ++客户端,我有些困惑。我的服务器是C#,所以这里的问题纯粹是客户端以异步方式向服务器发送请求。
作为参考,这是我设置客户端代码以发出异步请求的方式:
template<typename ResponseType, typename AsyncOpExecutor>
bool DoAsyncOp(const AsyncOpExecutor& op, const unsigned int deadlineMs, ResponseType& response)
{
grpc::ClientContext ctx;
grpc::CompletionQueue queue;
const std::unique_ptr<grpc::ClientAsyncResponseReaderInterface<ResponseType>> asyncOpResponse = op(ctx, queue);
grpc::Status status;
int requestTag = 1;
asyncOpResponse->Finish(&response, &status, (void*)requestTag);
bool result = false;
bool gotEvent = false;
do
{
const std::chrono::time_point<std::chrono::system_clock> deadline = std::chrono::system_clock::now() + std::chrono::milliseconds(deadlineMs);
void* got_tag;
bool ok = false;
const grpc::CompletionQueue::NextStatus nextStatus = queue.AsyncNext(&got_tag, &ok, deadline);
switch (nextStatus)
{
case grpc::CompletionQueue::NextStatus::TIMEOUT:
continue;
case grpc::CompletionQueue::NextStatus::GOT_EVENT:
assert(got_tag == (void*)requestTag);
// ok is always true even if I close the server while request is in progress.
assert(ok);
result = status.ok();
gotEvent = true;
break;
// Given that I am creating a new CompletionQueue per request (not using a shared one), is this flag likely to occur?
case grpc::CompletionQueue::NextStatus::SHUTDOWN:
result = false;
gotEvent = false;
break;
default:
result = false;
gotEvent = false;
break;
}
} while (!gotEvent);
return result;
}
我的第一个困惑是设置CompletionQueue
的最佳方法。 This answer似乎建议可以在请求中使用单个CompletionQueue
。如果多个线程使用同一队列发出请求,这将如何表现。假设我将上面的代码更改为使用共享队列,而不是为每个请求创建一个新队列。
一个线程如何知道它收到的响应是针对它的,而不是针对另一个线程的?
我是否需要为每个线程分配一个唯一的标签,并在每个线程上检查从队列接收的标签是否与我最初发送的标签匹配?
如果线程A收到了用于线程B的标签,这是否意味着线程B以后可以查询其标签,还是该标签丢失了,因为线程A首先看到了它?
实际上是否存在每个请求使用新队列而不是共享的主要问题?
我的第二个困惑是关于grpc::CompletionQueue::NextStatus::SHUTDOWN
的结果。如果我对每个请求使用一个新的队列,而不在队列上显式调用shutdown
,是否有可能发生这种结果?如果是,将触发什么?我进行的一项测试是在请求进行过程中关闭服务器,但是我没有得到grpc::CompletionQueue::NextStatus::GOT_EVENT
结果,而是将状态设置为UNAVAILABLE
。{p>
我最后的困惑是围绕ok
标志。我已经读过this answer,但是仍然不太清楚。给定上面发布的用例和代码,如果我从队列中得到的结果为grpc::CompletionQueue::NextStatus::GOT_EVENT
,则ok
标志可以为false,如果是,则将导致它为false?同样,这完全是围绕客户端,而不是服务器上CompletionQueue
的处理方式。
答案 0 :(得分:2)
您需要在某个时候调用Shutdown,然后清空队列。这是API的标准。否则,特别是对于服务器CQ,可能会发生泄漏。通过消耗,我的意思是调用Next,直到它返回false(或调用AsyncNext,直到它返回SHUTDOWN)。
对于流调用,服务器端调用等,GOT_EVENT的ok可以为false。如果仅查看客户端一元调用(如您的示例),则不会为false。错误的ok本质上意味着RPC的这一面已损坏。有关此文档的信息位于CQ的头文件中。