我需要在单线程应用程序中使用gRPC (带有额外的套接字通道)。我天真地考虑使用select(),并根据弹出的文件描述符调用gRPC来处理消息。我的问题是,有人能给我一个粗略的(5-10行代码)框架,告诉我在select()弹出后我需要调用什么?
在同步情况下,Google的"hello world“示例暗示了一个线程池(我不能使用它),而在异步情况下,显示了主循环阻塞--这对我不起作用,因为我需要处理其他套接字操作。
发布于 2016-09-28 06:49:12
在这一点上,你不能这样做(可能永远都不会)。
事件循环的一大弱点,包括直接使用select()/poll()风格的API,是它们不能以任何自然的方式组合,除非两者之间的直接集成。
从理论上讲,我们可以为Linux添加这样的功能--导出一个带有定时器and的epoll_fd,如果调用完成队列是有效的,那么这个定时器就会变得可读,但是这样做会对堆栈的其余部分施加大量的约束和架构开销,仅仅是为了在Linux上支持这个用例。其他任何地方都需要一个后台线程来管理fd的可读性。
发布于 2022-02-09 06:33:58
这可以使用gRPC异步服务和grpc::Alarm将来自select或其他轮询API的任何事件发送到gRPC完成队列。您可以在this gist中看到同时使用Epoll和gRPC的示例。重要的功能是这两个:
bool grpc_tick(grpc::ServerCompletionQueue& queue) {
void* tag = nullptr;
bool ok = false;
auto next_status = queue.AsyncNext(&tag, &ok, std::chrono::system_clock::now());
if (next_status == grpc::CompletionQueue::GOT_EVENT) {
if (ok && tag) {
static_cast<RequestProcessor*>(tag)->grpc_queue_tick();
} else {
std::cerr << "Not OK or bad tag: " << ok << "; " << tag << std::endl;
return false;
}
}
return next_status != grpc::CompletionQueue::SHUTDOWN;
}
bool tick_loops(int epoll, grpc::ServerCompletionQueue& queue) {
// Pump epoll events over to gRPC's completion queue.
epoll_event event{0};
while (epoll_wait(epoll, &event, /*maxevents=*/1, /*timeout=*/0)) {
grpc::Alarm alarm;
alarm.Set(&queue, std::chrono::system_clock::now(), event.data.ptr);
if (!grpc_tick(queue)) return false;
}
// Make sure gRPC gets at least 1 tick.
return grpc_tick(queue);
}在这里,您可以看到tick_loops函数反复调用epoll_wait,直到不再返回任何事件。对于每个epoll事件,都会构造一个grpc::Alarm,并将截止日期设置为now。在此之后,将立即使用grpc_tick抽出gRPC事件循环。
请注意,grpc::Alarm实例的生存时间必须超过其在完成队列中的时间。在真实的应用程序中,应该以某种方式将警报附加到标记(本例中为event.data.ptr),以便在完成回调中将其清除。
然后再次抽出gRPC事件循环,以确保也处理任何非epoll事件。
完成队列是线程安全的,因此您还可以将epoll泵放在一个线程上,将gRPC泵放在另一个线程上。使用此设置,您不需要像本例中那样将每个轮询超时设置为0。这将通过限制事件循环泵的干周期来减少CPU使用量。
https://stackoverflow.com/questions/39624822
复制相似问题