控制台应用main函数执行完成后进程仍未终止的问题排查求助
嘿,这个问题我之前碰过类似的,大概率是gRPC的后台线程在“拖后腿”!给你分析下可能的原因,再分享几个排查和解决的思路:
最可能的根因:gRPC后台线程未正常退出
gRPC内部会启动不少后台线程,比如DNS解析线程、连接池管理线程这些。当你创建channel的时候,这些线程就可能被触发启动了——哪怕你调用channel.reset()释放了通道实例,有些后台线程可能还在执行未完成的操作(比如你这里解析一个不存在的域名,系统DNS的重试逻辑可能还在后台跑),导致进程没法正常退出。
具体的排查与解决步骤:
强制清理gRPC全局资源
在return 0;之前,试试调用grpc::ShutdownBlockingly();,这个函数会强制gRPC关闭所有后台线程和全局资源,等所有线程都退出后再返回。修改后的代码如下:#include <iostream> #include <grpcpp/grpcpp.h> int main() { _putenv("GRPC_DNS_RESOLVER=native"); auto channel = grpc::CreateChannel("thishostdoesnotexists123.in:9000", grpc::InsecureChannelCredentials()); channel.reset(); std::cout << "end " << channel.use_count() << std::endl; grpc::ShutdownBlockingly(); // 新增这一行,强制清理gRPC后台资源 return 0; }这个方法大概率能直接解决进程挂住的问题。
用调试工具定位卡住的线程
如果上面的方法没用,就需要精准定位哪个线程在“赖着不走”。打开Visual Studio(或者WinDbg),当进程卡住时暂停调试,打开「线程」窗口,查看每个线程的调用栈——比如如果是DNS解析线程卡在了系统的DNS函数里,就能一眼定位到问题点。调整DNS解析的超时逻辑
你设置了GRPC_DNS_RESOLVER=native,用的是Windows系统自带的DNS解析器。解析不存在的域名时,系统可能会有多次重试,这个过程会在后台线程持续进行。可以试试:- 通过
grpc::ChannelArguments设置DNS超时时间,缩短等待时长:grpc::ChannelArguments args; args.SetInt(GRPC_ARG_DNS_RESOLVER_TIMEOUT_MS, 1000); // 设置1秒超时 auto channel = grpc::CreateChannel("thishostdoesnotexists123.in:9000", grpc::InsecureChannelCredentials(), args); - 去掉
GRPC_DNS_RESOLVER=native环境变量,改用gRPC默认的ares解析器,看看是否还会出现挂住的情况。
- 通过
开启gRPC日志查看细节
设置两个环境变量:GRPC_VERBOSITY=DEBUG和GRPC_TRACE=dns_resolver,thread_pool,然后运行程序。日志会详细记录gRPC内部的线程活动和DNS解析过程,能帮你找到到底是哪个步骤卡住了。
补充说明
你调用channel.reset()后use_count()显示0,说明channel实例确实被释放了,但gRPC的全局后台线程不受单个channel的生命周期控制——所以哪怕channel没了,这些线程可能还在运行,ShutdownBlockingly()就是专门用来清理这类全局资源的。
备注:内容来源于stack exchange,提问作者Joe J

