这个图像检索服务器在服务端的工作:1.接收客户端发来的图片+2.计算图片特征向量,匹配特征库图像+3.发回topK的相似图片。
上版的做法:一个eopll主线程循环检测是否有连接进来==》检测到连接,装填读任务队列==》读任务调度线程循环检测是否有读任务==》检测到读任务,分派工作线程去处理==》工作线程读完客户端发来的图片,装填计算图片片特征向量,匹配特征库图像队列===》计算队列调度线程检测到计算任务,分派工作线程去处理==》。。。。
将任务拆分成了3级任务队列:读图片(占用IO)+计算特征向量和计算相似度(占用cpu)+写相似度topK的图片给客户端(占用IO)。原本的意愿是希望某个线程在读图片的过程中(占用IO),其他线程可以去计算特征向量和计算相似度(占用cpu),以此做成流水线,因为线程池的线程个数总是有限的,如果做成单任务队列形式的话,会出现明明IO空闲着,但因为线程池没有线程导致不能利用这段空闲时间去读图片。【计算任务的耗时远大于读,写图片】如图,当初的想法:
想法是好的,但是我的三个任务队列同用一个线程池,那就有问题了。当读任务队列,计算任务队列,写任务队列都有任务时,这个唤醒线程是去读?还是写?还是计算?只有鬼知道了。极端情况,假如我有4核cpu,可以同时处理四个计算任务;现在有6个线程,唤醒后都被拿去处理计算任务,那么cpu会频繁的切换这6个线程,因为cpu核只有4个。再假设我将线程数设置成cpu核数4,这固然可以解决cpu频繁切换的问题,但是还是上面的问题,极端情况,4个线程都被拿去处理计算任务了,那么一堆的写回任务堆积在那边,没有处理,服务器处理整个任务的时长依然很长。没有完全达到流水的目的。
改进的想法是:读图片、计算任务、写回图片均有自己的线程池。
读、写的线程池中线程个数均为1:计算任务耗时远大于读、写,所以读写即使是阻塞的读,阻塞的写都没关系,因为计算任务等的起。
计算特征向量的线程池中的线程个数为4(cpu核数):>4后频繁切换
另:读、写的线程无任务时用条件变量阻塞(或用sleep,但是sleep的坏处是首次唤醒必须等待一段时间),如此防止空转。