程序死锁如何定位

xian_wwq 2014-11-27 03:47:27
在调试多线程服务时,发生死锁。
目前想到的办法:
1.阅读代码,逐一检查加锁逻辑
2. 注释部分功能代码,分别测试
但总体感觉这些方法比较费时

请教大牛,还有没有高效的方法
-----------------
网上有人推荐使用LockCop检测,
但因程序内启用的线程较多,而LockCop仅显示thread id 和状态,
感觉也不太好找

...全文
816 16 打赏 收藏 转发到动态 举报
写回复
用AI写文章
16 条回复
切换为时间正序
请发表友善的回复…
发表回复
赵4老师 2014-12-01
  • 打赏
  • 举报
回复
引用 15 楼 xian_wwq 的回复:
经过加班,终于找到问题了。 问题出的也算简单,自己写的模块调用了数据解析的回调,回调是另一个同事写的,他在数据处理中又调用了我的模块的一个函数 ,由于这个函数在处理时,锁域过大,就会导致极小概率下的double lock了。 ------------- 小结: 1. windbg真的很好用,可以清楚的查询到锁争用的线程信息 2. 对外提供的接口函数,最好是无锁的 3. 锁域不能太大,特别是不能在锁中轻易使用回调 谢谢大家,结贴
一个官方Debug工具都不好用的OS能算作可用的OS吗?!
xian_wwq 2014-12-01
  • 打赏
  • 举报
回复
经过加班,终于找到问题了。 问题出的也算简单,自己写的模块调用了数据解析的回调,回调是另一个同事写的,他在数据处理中又调用了我的模块的一个函数 ,由于这个函数在处理时,锁域过大,就会导致极小概率下的double lock了。 ------------- 小结: 1. windbg真的很好用,可以清楚的查询到锁争用的线程信息 2. 对外提供的接口函数,最好是无锁的 3. 锁域不能太大,特别是不能在锁中轻易使用回调 谢谢大家,结贴
xian_wwq 2014-11-30
  • 打赏
  • 举报
回复
引用 2 楼 xian_wwq 的回复:
[quote=引用 1 楼 baijiaheizhiganmao 的回复:] 方法三:添加printf语句,看看到了那一条之后就不再printf了 程序死锁应该是程序逻辑上出现了问题。没有更好的方法。按照你自己的方法一步一步来吧。
多谢 刚用LockCop看了下线程状态,还没有出现thread deadlock的问题。 只能逐一排查了。[/quote] 自己把自己误导了,因为工程中没有用临界区而是自旋锁,所以没有查到临界区deadlock信息
xian_wwq 2014-11-28
  • 打赏
  • 举报
回复
后台使用的spthread线程池, 目前观察到,一旦线程池的可分配线程涨到上限(100)就会发生死锁 个人估计是线程中处理逻辑有问题,导致线程无法及时完成,释放出可用的线程 根据大家的建议,再分块跟踪下
ztenv 版主 2014-11-28
  • 打赏
  • 举报
回复
1、可以通过日志定位 2、可以使用工具看线程及锁信息 3、根据问题基本定位代码范围,结合出现问题的现象,适合的给出代码执行的序列 4、其他
w74839520 2014-11-27
  • 打赏
  • 举报
回复
好贴,学习如何调试多线程。
漂流的代码 2014-11-27
  • 打赏
  • 举报
回复
代码推演后,在可疑的地方打log,然后再验证。重复若干次就可以了。 找bug这种事情,效率依赖于好的工具+好的方法。最根本的还是分离所有无关代码。
fly_dragon_fly 2014-11-27
  • 打赏
  • 举报
回复
死锁后,中断下来,一个个线程堆栈找过来,然后再分析死锁的原因
FightForProgrammer 2014-11-27
  • 打赏
  • 举报
回复
个人感觉打印线程id以及执行过程到文件中也是可以采用的办法
zhousitiaoda 2014-11-27
  • 打赏
  • 举报
回复
磨刀不误砍柴工,先看下代码,看有没有可能交叉加锁的情况。
赵4老师 2014-11-27
  • 打赏
  • 举报
回复
Process Explorer 找出进程打开了哪些文件、注册表项和其他对象,已加载哪些 DLL 等信息。这个功能异常强大的实用工具甚至可以显示每个进程的所有者。http://www.microsoft.com/china/technet/sysinternals/utilities/ProcessExplorer.mspx
principl 2014-11-27
  • 打赏
  • 举报
回复
死锁检测,代码走读+分析貌似是最有效的方法。
赵4老师 2014-11-27
  • 打赏
  • 举报
回复
为避免死锁,请对所有多重加锁的语句都按照相同顺序加锁。比如 Thread1: lock A lock B unlock B unlock A Thread2: 不要 lock B lock A unlock A unlock B 而要 lock A lock B unlock B unlock A 有时不将“调用函数名字+各参数值,进入函数后各参数值,中间变量值,退出函数前准备返回的值,返回函数到调用处后函数名字+各参数值+返回值”这些信息写日志到文件中是无论如何也发现不了问题在哪里的,包括捕获各种异常、写日志到屏幕、单步或设断点或生成core文件、……这些方法都不行! 写日志到文件参考下面:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef WIN32
    #include <windows.h>
    #include <io.h>
#else
    #include <unistd.h>
    #include <sys/time.h>
    #include <pthread.h>
    #define  CRITICAL_SECTION   pthread_mutex_t
    #define  _vsnprintf         vsnprintf
#endif
//Log{
#define MAXLOGSIZE 20000000
#define MAXLINSIZE 16000
#include <time.h>
#include <sys/timeb.h>
#include <stdarg.h>
char logfilename1[]="MyLog1.log";
char logfilename2[]="MyLog2.log";
static char logstr[MAXLINSIZE+1];
char datestr[16];
char timestr[16];
char mss[4];
CRITICAL_SECTION cs_log;
FILE *flog;
#ifdef WIN32
void Lock(CRITICAL_SECTION *l) {
    EnterCriticalSection(l);
}
void Unlock(CRITICAL_SECTION *l) {
    LeaveCriticalSection(l);
}
#else
void Lock(CRITICAL_SECTION *l) {
    pthread_mutex_lock(l);
}
void Unlock(CRITICAL_SECTION *l) {
    pthread_mutex_unlock(l);
}
#endif
void LogV(const char *pszFmt,va_list argp) {
    struct tm *now;
    struct timeb tb;

    if (NULL==pszFmt||0==pszFmt[0]) return;
    _vsnprintf(logstr,MAXLINSIZE,pszFmt,argp);
    ftime(&tb);
    now=localtime(&tb.time);
    sprintf(datestr,"%04d-%02d-%02d",now->tm_year+1900,now->tm_mon+1,now->tm_mday);
    sprintf(timestr,"%02d:%02d:%02d",now->tm_hour     ,now->tm_min  ,now->tm_sec );
    sprintf(mss,"%03d",tb.millitm);
    printf("%s %s.%s %s",datestr,timestr,mss,logstr);
    flog=fopen(logfilename1,"a");
    if (NULL!=flog) {
        fprintf(flog,"%s %s.%s %s",datestr,timestr,mss,logstr);
        if (ftell(flog)>MAXLOGSIZE) {
            fclose(flog);
            if (rename(logfilename1,logfilename2)) {
                remove(logfilename2);
                rename(logfilename1,logfilename2);
            }
        } else {
            fclose(flog);
        }
    }
}
void Log(const char *pszFmt,...) {
    va_list argp;

    Lock(&cs_log);
    va_start(argp,pszFmt);
    LogV(pszFmt,argp);
    va_end(argp);
    Unlock(&cs_log);
}
//Log}
int main(int argc,char * argv[]) {
    int i;
#ifdef WIN32
    InitializeCriticalSection(&cs_log);
#else
    pthread_mutex_init(&cs_log,NULL);
#endif
    for (i=0;i<10000;i++) {
        Log("This is a Log %04d from FILE:%s LINE:%d\n",i, __FILE__, __LINE__);
    }
#ifdef WIN32
    DeleteCriticalSection(&cs_log);
#else
    pthread_mutex_destroy(&cs_log);
#endif
    return 0;
}
//1-78行添加到你带main的.c或.cpp的那个文件的最前面
//81-85行添加到你的main函数开头
//89-93行添加到你的main函数结束前
//在要写LOG的地方仿照第87行的写法写LOG到文件MyLog1.log中
shiguojie19892 2014-11-27
  • 打赏
  • 举报
回复
看到会写多线程的我就想膜拜呀
xian_wwq 2014-11-27
  • 打赏
  • 举报
回复
引用 1 楼 baijiaheizhiganmao 的回复:
方法三:添加printf语句,看看到了那一条之后就不再printf了 程序死锁应该是程序逻辑上出现了问题。没有更好的方法。按照你自己的方法一步一步来吧。
多谢 刚用LockCop看了下线程状态,还没有出现thread deadlock的问题。 只能逐一排查了。
忘世麒麟 2014-11-27
  • 打赏
  • 举报
回复
方法三:添加printf语句,看看到了那一条之后就不再printf了 程序死锁应该是程序逻辑上出现了问题。没有更好的方法。按照你自己的方法一步一步来吧。
《计算机操作系统》可作为计算机硬件和软件以及计算机通信专业的本科生教材,也可作为从事计算机及通信工作的相关科技人员的参考书。 目录 第一章 操作系统引论 1.1 操作系统的目标和作用 1 1.1.1 操作系统的目标 1 1.1.2 操作系统的作用 2 1.1.3 推动操作系统发展的主要动力 4 1.2 操作系统的发展过程 5 1.2.1 无操作系统的计算机系统 5 1.2.2 单道批处理系统 6 1.2.3 多道批处理系统 7 1.2.4 分时系统 9 1.2.5 实时系统 11 1.2.6 微机操作系统的发展 12 1.3 操作系统的基本特性 14 1.3.1 并发性 14 1.3.2 共享性 15 1.3.3 虚拟技术 16 1.3.4 异步性 17 1.4 操作系统的主要功能 18 1.4.1 处理机管理功能 18 1.4.2 存储器管理功能 19 1.4.3 设备管理功能 21 1.4.4 文件管理功能 21 1.4.5 操作系统与用户之间的接口 22 1.5 OS结构设计 24 1.5.1 传统的操作系统结构 24 1.5.2 客户/服务器模式 26 1.5.3 面向对象的程序设计 27 1.5.4 微内核OS结构 29 习题 33 第二章 进 程 管 理 2.1 进程的基本概念 34 2.1.1 程序的顺序执行及其特征 34 2.1.2 前趋图 35 2.1.3 程序的并发执行及其特征 36 2.1.4 进程的特征与状态 37 2.1.5 进程控制块 41 2.2 进程控制 43 2.2.1 进程的创建 43 2.2.2 进程的终止 45 2.2.3 进程的阻塞与唤醒 46 2.2.4 进程的挂起与激活 47 2.3 进程同步 47 2.3.1 进程同步的基本概念 47 2.3.2 信号量机制 50 2.3.3 信号量的应用 53 2.3.4 管程机制 55 2.4 经典进程的同步问题 58 2.4.1 生产者—消费者问题 58 2.4.2 哲学家进餐问题 61 2.4.3 读者—写者问题 63 2.5 进程通信 65 2.5.1 进程通信的类型 65 2.5.2 消息传递通信的实现方法 66 2.5.3 消息传递系统实现中的若干问题 68 2.5.4 消息缓冲队列通信机制 69 2.6 线程 71 2.6.1 线程的基本概念 72 2.6.2 线程间的同步和通信 75 2.6.3 线程的实现方式 77 2.6.4 线程的实现 78 习题 81 第三章 处理机调度与死锁 3.1 处理机调度的层次 84 3.1.1 高级调度 84 3.1.2 低级调度 86 3.1.3 中级调度 87 3.2 调度队列模型和调度准则 88 3.2.1 调度队列模型 88 3.2.2 选择调度方式和调度算法的若干准则 90 3.3 调度算法 91 3.3.1 先来先服务和短作业(进程)优先调度算法 91 3.3.2 高优先权优先调度算法 93 3.3.3 基于时间片的轮转调度算法 95 3.4 实时调度 97 3.4.1 实现实时调度的基本条件 97 3.4.2 实时调度算法的分类 99 3.4.3 常用的几种实时调度算法 100 3.5 产生死锁的原因和必要条件 103 3.5.1 产生死锁的原因 103 3.5.2 产生死锁的必要条件 105 3.5.3 处理死锁的基本方法 105 3.6 预防死锁的方法 106 3.6.1 预防死锁 106 3.6.2 系统安全状态 107 3.6.3 利用银行家算法避免死锁 108 3.7 死锁的检测与解除 111 3.7.1 死锁的检测 111 3.7.2 死锁的解除 113 习题 114 第四章 存 储 器 管 理 4.1 存储器的层次结构 116 4.1.1 多级存储器结构 116 4.1.2 主存储器与寄存器 117 4.1.3 高速缓存和磁盘缓存 117 4.2 程序的装入和链接 118 4.2.1 程序的装入 118 4.2.2 程序的链接 120 4.3 连续分配方式 121 4.3.1 单一连续分配 121 4.3.2 固定分区分配 122 4.3.3 动态分区分配 123 4.3.4 伙伴系统 126 4.3.5 哈希算法 126 4.3.6 可重定位分区分配 127 4.3.7 对换 129 4.4 基本分页存储管理方式 130 4.4.1 页面与页表 130 4.4.2 地址变换机构 131 4.4.3 两级和多级页表 133 4.5 基本分段存储管理方

64,654

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧