问题背景
最近在用QT开发一个简单的网络图片播放器,即通过UDP协议接收网络图片数据,并展示接收到的图片数据。
QT程序设计成多线程,主线程为UI线程,只负责界面的维护;两个子线程,一个为UDP线程,负责接收网络图片数据,一个为显示线程,负责对图片数据进行必要的处理,然后送回主线程显示。
主界面设计了一个保存图像的按钮,点击一下可以将当前界面显示的图像保存成bmp格式的图片。
在调试的过程中,出现了这样的问题:
拖动UI界面或改变UI界面大小,或点击保存图像时,UDP接收会出现丢包的现象。
解决过程
凭着曾经多线程的经验和调UDP的经验,第一直觉是UDP接收端丢包了。但是最开始没有设计保存图像的按钮,光改变UI界面也会丢包。自己也进行了一些实验,例如将发送端速度从快到慢调整,发现发送速度慢的时候,不会丢包。因此,根据经验,判定UDP丢包多半是由于接收端处理不过来的原因导致的。
由于自己是第一次开发QT,于是向前辈们请教了一下。前辈们大概给了几下几点解决的建议:
- 不要用QT封装的UDP,比较慢,建议用系统原生的UDP套接字函数。
- 增大UDP的接收缓冲区。
- UDP多收一些数据,再调用锁,将收到的数据写到公共buffer中。这样减少锁的次数,可以提高程序效率。
- 将多线程分别绑定到不同的CPU核心。
分别尝试了以上的解决办法,最后通过将多线程分别绑定到不同的CPU核心,解决了UI界面改变或保存图像时的UDP丢包问题。
实现方法
在linux系统下,可以通过设置某个线程的CPU亲和性,来将某个线程绑定到特定的CPU上。
其实现方式如下:
#include <unistd.h>
#include <sched.h>
#include <thread>
void thread_function1()
{
cpu_set_t mask; //CPU亲和力掩码
CPU_ZERO(&mask); //清空集合
CPU_SET(1, &mask); //将该进程绑定到CPU core 1上
if(pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask) < 0) //对应的是sched_setaffinity()系统调用
{
printf("thread set to cpu 1 failed\n\r");
}
/*
your code
*/
}
在本QT程序中,将udp线程和display线程分别通过上述方式绑定到了不同的CPU核心。


在Linux下,可以使用top工具,动态验证上述代码是否生效。
运行QT程序,假设其PID为1234,通过下述命令查看QT程序不同线程占用的CPU。
top -p 1234
f
方向键调整到P = Last User Cpu (SMP)
空格
s
Esc
H
本例中,通过上述操作得到如下图所示的结果。

观察图片最右侧的P列,可以看到在整个程序运行过程中,udpThread一直占据1号CPU,picDisplay一直占据2号CPU。实现了线程绑定到不同的CPU核心。
本文地址: 绑定thread到CPU不同核心