近日自己写的一个小工具,用到了Qt线程池QThreadPool和Redis,从QRunnable继承实现了一个Executor,Redis连接由一个自定义的Manager管理,根据当前所处的线程,自动创建连接,连接创建后缓存在Manager中,下次相同的线程再执行任务时,使用之前创建的Redis连接进行操作。
使用中有时会出现Redis连接发出数据后,收不到回复的情况,多次试验后发现如果执行一次后,经过30秒以上再执行,就会出现这个问题,30秒以内再次执行则没这个问题。
排查过程如下:
1.先是怀疑是socket的问题,但是抓包后发现socket数据已经返回了,只是应用没有收到数据。
2.跟踪Qt源码后发现,是系统没给程序发送读数据的消息,正常情况,发送数据会收到一个socket写数据消息,收到数据后会收到一个socket读数据消息,异常时只有写数据消息,没有读数据消息,为啥没消息卡了好长时间。
3.自己new了一个Thread,调用Executor,发现只能执行一次,第二次再执行就无法收到数据,与时间无关,跟踪源码发现Qt的线程每次start都会重新创建一个系统原生线程,但是socket是在第一个执行时创建的,也就是说创建socket的系统线程在第一次执行完后就销毁了,后面再执行时的系统线程不是创建socket的系统线程,估计就是因为线程不同导致系统消息没发送到应用中,但是为什么写数据的消息能收到,不太清楚。
4.查看QThreadPool代码,和推断的的一致,一个线程空闲超过30秒时,ThreadPool会将系统线程释放,只保留QThread对象,再次使用该QThread时会重新创建系统线程,其中30秒的超时时间可以通过setExpiryTimeout函数进行设置,设置值小于0时,始终不会释放系统线程,直到QThread被释放。
综上,如果线程中用到的资源不是每次都重新创建,则需要注意其生命周期,注意是否会因为系统线程的释放导致异常结果。