问题背景

最近在用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核心。

说点什么
请文明发言!
支持Markdown语法
好耶,沙发还空着ヾ(≧▽≦*)o
Loading...