某日,在浙大国家实验室,与老方和小崔调试监控死锁问题。机柜里一溜架装服务器上出现死锁问题。用WinDbg看,发现其中导致死锁的临界区LockCount值是小于-1的数!!
 
多次重现该问题,发现LockCount经常是负的两三百。
我等本着不十分科学严谨,但又有一点科学严谨的态度,装模作样查了下资料,显示如下:
 
LockCount代表什么含义
 
ms-help://MS.MSDNQTR.v80.en/MS.MSDN.v80/dnmag03/html/CriticalSections1203default.htm

http://msdn.microsoft.com/zh-cn/magazine/cc164040(en-us).aspx
 
struct RTL_CRITICAL_SECTION
{
    PRTL_CRITICAL_SECTION_DEBUG DebugInfo;
    LONG LockCount;
    LONG RecursionCount;
    HANDLE OwningThread;
    HANDLE LockSemaphore;
    ULONG_PTR SpinCount;
};
 
LockCount
这是临界区里最重要的字段。其初始值为-1,而0或更大的值表示临界区被持有。当该值不等于-1,OwningThread字段(该字段在WinNT.h里定义错误的,应该用DWORD而不是HANDLE类型)存放了持有该临界区的线程ID。
LockCount - (RecursionCount - 1 ) 表示还有多少其他线程在等待获取该临界区。
 
(以下是英文原版)
LockCount
This is the most important field in a critical section. It is initialized to a value of -1; a value of 0 or greater indicates that the critical section is held or owned. When it's not equal to -1, the OwningThread field (this field is incorrectly defined in WINNT.H—it should be a DWORD instead of a HANDLE) contains the thread ID that owns this critical section. The delta between this field and the value of (RecursionCount -1) indicates how many additional threads are waiting to acquire the critical section.
 
 
LockCount的值是如何变化的。
 
网上有很多文章根据临界区的原理,总结了两个能使LockCount变换的函数的伪代码如下:
 
_RtlTryEnterCriticalSection
 
if(CriticalSection->LockCount == -1)
{
  // 临界区可用
  CriticalSection->LockCount = 0;
  CriticalSection->OwningThread = TEB->ClientID;
  CriticalSection->RecursionCount = 1;
 
  return TRUE;
}
else
{
  if(CriticalSection->OwningThread == TEB->ClientID)
  {
    // 临界区是当前线程获取
    CriticalSection->LockCount++;
    CriticalSection->RecursionCount++;
 
    return TRUE;
  }
  else
  {
    // 临界区已被其它线程获取
    return FALSE;
  }
}
 
 
_RtlLeaveCriticalSection
 
if(--CriticalSection->RecursionCount == 0)
{
  // 临界区已不再被使用
  CriticalSection->OwningThread = 0;
 
  if(--CriticalSection->LockCount)
  {
    // 仍有线程锁定在临界区上
    _RtlpUnWaitCriticalSection(CriticalSection)
  }
}
else
{
  --CriticalSection->LockCount
}
 
上述文字中的含义可以比较清晰地推断出:
1.       RecursionCount有可能由于LeaveCriticalSection的多余调用而小于初值0 (已经实证)
2.       LockCount的值只可能大于或等于初值-1
 
理论似乎再一次与事实不符!
 
我们开始胡思乱想,猜测如下几种可能:
1.       EnterCriticalSection执行到一半异常中止
这种机会很小,即使发生,也找不出什么道理让LockCount变成负两三百这么离谱。
2.       内存错乱导致RTL_CRITICAL_SECTION结构被写坏。
 
但几种推测都查证无果。
 
一个偶然的机会 -_-!!! ,我在自己的计算机上实验的时候,居然也发现了LockCount小于-1!而且屡试不爽!
我的计算机装的Windows Vista,我们自然就有如下猜想:
在某个操作系统版本下,LockCount的机制本来就有所不同!!
 
这个猜想比较靠谱,立刻着手验证。实验室里发生这个问题的电脑都是Windows2003+SP1。我们马上在Windows2003+SP1系统做了测试,写了个非常简单的测试,创建一个临界区,然后调用EnterCriticalSection,果然发现LockCount编程了-2!而多线程下测试,也确实会出现负两三百的情况。
看来LockCount的含义在不同版本的Win下确实不一样。
其后我们多次尝试上网搜索关于LockCount含义在Windows不同版本中的变迁,却不得要领。
又一个偶然的机会 -_-!!! ,老方在WinDbg的帮助文档里发现了一段关于LockCount变迁的说明,全文如下 (真是踏破铁鞋无觅处,得来全不费工夫)
 
 
Interpreting Critical Section Fields in Windows Server 2003 SP1 and Later
 
In Microsoft Windows Server 2003 Service Pack 1 and later versions of Windows, the LockCount field is parsed as follows:
 
The lowest bit shows the lock status. If this bit is 0, the critical section is locked; if it is 1, the critical section is not locked.
The next bit shows whether a thread has been woken for this lock. If this bit is 0, then a thread has been woken for this lock; if it is 1, no thread has been woken.
The remaining bits are the ones-complement of the number of threads waiting for the lock.
 
As an example, suppose the LockCount is -22. The lowest bit can be determined in this way:
 
0:009> ? 0x1 & (-0n22)
uate expression: 0 = 00000000
 
The next-lowest bit can be determined in this way:
 
0:009> ? (0x2 & (-0n22)) >> 1
uate expression: 1 = 00000001
 
The ones-complement of the remaining bits can be determined in this way:
 
0:009> ? ((-1) - (-0n22)) >> 2
uate expression: 5 = 00000005
 
In this example, the first bit is 0 and therefore the critical section is locked. The second bit is 1, and so no thread has been woken for this lock. The complement of the remaining bits is 5, and so there are five threads waiting for this lock.

事情至此总算水落石出!

总结一下 等待线程数量:(-1 - LockCount) >> 2

查看临界区等待线程数量相关推荐

  1. Java多线程 信号量和屏障实现控制并发线程数量,主线程等待所有线程执行完毕2

    项目需求需要多线程执行计算任务,主线程要等所有线程执行完毕,记录执行结果.另外要控制并行线程数量,防止用光内存.实现测试代码如下. 测试代码如下: public class ThreadsTest { ...

  2. Python 多线程总结(2)— 线程锁、线程池、线程数量、互斥锁、死锁、线程同步

    主要介绍使用 threading 模块创建线程的 3 种方式,分别为: 创建 Thread 实例函数 创建 Thread 实例可调用的类对象 使用 Thread 派生子类的方式 多线程是提高效率的一种 ...

  3. java——自己实现基础的线程池及带有任务数过多拒绝策略、线程池销毁、自动扩充线程数量及闲时自动回收线程等操作的改进版线程池

    1. 实现第一版基础的线程池 1.1 首先我们定义一个线程池类ThreadPool,然后线程池有一个容器存放我们创建的线程,另一个容器则是存放当前线程池需要处理的任务队列,线程容器用ArrayList ...

  4. linux下查看进程的线程数,linux查看进程的线程数

    top -H -p $PID  #查看对应进程的那个线程占用CPU过高 1.top -H 手册中说:-H : Threads toggle 加上这个选项启动top,top一行显示一个线程.否则,它一行 ...

  5. linux命令(一)查看进程的线程数top,ps

    1.ps -T -p <pid> ps -T -p 116115|wc -l 2. top -H -p 116115 查看进程的线程使用率 3.pstree -p 116115|wc -l ...

  6. [Linux]线程概念_线程控制(线程与进程的区别与联系 | 线程创建 | 线程等待 | 线程终止 | 线程分离 | LWP)

    文章目录 线程概念 进程和线程的关系 线程的优点 线程的缺点 线程控制 Linux线程和接口关系的认识 线程创建 线程ID及进程地址空间布局 线程等待 线程终止 线程终止状态 线程分离 LWP和pth ...

  7. CentOS查看进程的线程数方法

    CentOS 查看某个进程的线程 方法一 ps -T -p <pid> 1 方法二 top -H -p <pid> 方式一: cat /proc/[pid]/status 展示 ...

  8. 教你控制Python多线程中线程数量

    前言 前段时间学习了python的多线程爬虫,当时爬取一个图片网站,开启多线程后,并没有限制线程的数量,也就是说,如果下载1000张图片,会一次性开启1000个子线程同时进行下载 现在希望控制线程数量 ...

  9. c++线程数量的限制

    C++线程数量的限制 限制 修改限制 限制 线程的数量取决于线程栈空间的大小(可以使用ulimit -s查看栈空间大小) 132位Linux下(可以使用getconf LONG_BIT查看当前CPU运 ...

最新文章

  1. 手写Redis服务端,从设计者的角度聊一聊Redis本身
  2. 交换机运维需要注意哪些问题,让我们一起来闲聊下
  3. Vue-cli代理解决跨域问题
  4. Java类加载器总结
  5. 【课题总结】OpenCV 抠图项目实战(8)图像轮廓
  6. 获取skipcase
  7. 开始VC6学习之旅3
  8. Android studio 0.5.0 注意事项
  9. 惊叹 | 膜拜一下清华大学特等奖学金的学霸大佬们的简历! -- 我们没有理由不努力!...
  10. 如何在 think-cell 瀑布图中并行汇总多个系列?
  11. 产生随机数(C语言)
  12. [全网首发]坚果Pro3 root教程 Magisk
  13. 如何在WhatsApp中设置两步验证
  14. HYPERLEDGER FABRIC网络搭建之network e2ecli_default not found
  15. 全球首位亿万富豪上太空!
  16. Switch上gamemaker,6.11发售,可不写代码创造游戏
  17. 安装oracle gcc版本,安装cx_Oracle时报错:error: command 'gcc' failed with exit status 1
  18. 【PyTorch训练中Dataset多线程加载数据,比Dataloader里设置多个workers还要快】
  19. 单灯闪烁c语言程序,51单片机,C语言编程,控制指示灯闪烁的频率
  20. Android 第三方库AgentWeb的使用

热门文章

  1. 记录Flex布局的属性
  2. CDQZ集训DAY8 日记
  3. Hadoop学习笔记五
  4. iOS-----Xcode-Debug尝试
  5. [心得分享] 我在 GitHub 上学习开源
  6. Android Navigation Drawer(导航抽屉)
  7. MIME文件类型格式--汇总
  8. iptables使用ipt_connlimit限制连接数
  9. 网站内容才是SEO的第一要素
  10. WorldWind学习系列一:顺利起航篇