示例4:锁长期等待
简介
多线程程序,经常存在锁不合理长期占用的问题。本示例主要针对这一问题,通过鲲鹏DevKit系统性能分析工具资源调度功能来观测,建议用户分析业务逻辑将不需要锁的计算移出互斥量加锁操作。
环境准备
锁长期占用检测
- 程序准备。
- 编译pthread_mutex_long.c并赋予执行文件所有用户可读、可写和可执行权限。
1
gcc -g pthread_mutex_long.c -o pthread_mutex_long -lpthread -lm && chmod 777 pthread_mutex_long
若使用的GCC版本为10.x.x及以上,因该版本默认启用LSE指令集,可能优化效果不明显,建议编译程序时添加参数“-march=armv8-a+nolse -mno-outline-atomics”用以禁用该指令集。
- 绑核到CPU 0-3,使用后台运行程序,需要确定0-3核上没有其他程序正在绑核运行。nohup命令使得即使退出账户之后会继续运行相应的进程,防止任务中断。
1
nohup taskset -c 0-3 ./pthread_mutex_long >>pthread_mutex_long.out 2>&1 &
程序运行的标准输出(1)将会保存到pthread_mutex_long.out文件,错误信息(2)会重定向到pthread_mutex_long.out文件。
- 编译pthread_mutex_long.c并赋予执行文件所有用户可读、可写和可执行权限。
- 采用进程/线程性能分析,观察消耗CPU资源的进程。
创建进程/线程分析任务,并启动分析。
表1 任务配置参数说明 参数
说明
分析对象
系统
分析类型
进程/线程性能分析
采样时长
30秒
采样类型
全选
采集线程信息
开启
- 查看采集分析结果。
为便于观察消耗CPU资源的进程,可单击%CPU列旁边的
按钮进行降序排列,如图1所示,显示pthread_mutex_long程序在消耗大量的CPU资源。
- 采用系统级的资源调度分析,观察pthread_mutex_long程序进程和线程的切换次数及抢占情况。
创建系统级的资源调度分析任务,并启动分析。
表2 任务配置参数说明 参数
说明
分析对象
系统
分析类型
资源调度分析
采样时长
60秒
其他参数
默认
- 查看采集分析结果。
输入进程名单击搜索框旁的
按钮,筛选pthread_mutex_long进程,如图2所示。
- 在“CPU/进程统计”的页签中,查看进程/线程详情。图3 CPU/进程统计分析结果
CPU切换链路频繁极有可能是锁互斥量操作。其中一个线程长期占用锁,导致另一个线程无法得到锁而长期等待。
- 结束程序。
通过jobs -l查看pthread_mutex_long程序运行PID(如果程序尚未结束,可以看到后台运行的程序),通过kill -9结束进程。
1 2
jobs -l kill -9 <pthread_mutex_long程序PID>
优化方案
将不需要互斥的操作移到pthread_mutex_unlock()之后。
- 程序准备。
- 编译pthread_mutex_long_mod.c并赋予执行文件所有用户可读、可写、可执行权限。
1
gcc -g pthread_mutex_long_mod.c -o pthread_mutex_long_mod -lpthread -lm && chmod 777 pthread_mutex_long_mod
- 绑核到CPU 0-3,使用后台运行程序,需要确定0-3核上没有其他程序正在绑核运行。
1
nohup taskset -c 0-3 ./pthread_mutex_long_mod >>pthread_mutex_long_mod.out 2>&1 &
程序运行的标准输出(1)将会保存到pthread_mutex_long_mod.out文件,错误信息(2)会重定向到pthread_mutex_long_mod.out文件。
- 编译pthread_mutex_long_mod.c并赋予执行文件所有用户可读、可写、可执行权限。
- 采用系统级的资源调度分析。
创建系统级的资源调度分析任务,并启动分析。
表3 任务配置参数说明 参数
说明
分析对象
系统
分析类型
资源调度分析
采样时长
60秒
其他参数
默认
- 查看采集分析结果。
在搜索框中输入进程名后单击搜索框旁的
按钮,选择pthread_mutex_long_mod,如图4,发现优化后搜索不到对应信息,锁优化策略有效。
- 切换“CPU/进程统计”页签,查看线程情况。
如图5所示,进程切换链路明显减少,线程可以并发运行不需要互斥操作的计算,大幅度提升了计算性能。
- 结束样例程序。
通过jobs -l查看pthread_mutex_long_mod程序运行PID(如果程序尚未结束,可以看到后台运行的程序),通过kill -9结束进程。
1 2
jobs -l kill -9 <pthread_mutex_long_mod程序PID>