记一次旋转锁Bug

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Zealot_Alie/article/details/81138641

这几天同事在忙忙碌碌研究的一个奇怪的bug,最终发现问题出现在我写的一段代码上。

代码是一个HTTP与客户端交互的中间层,由于HTTP请求的回调不在主线程上,所以涉及到两个线程中间的交互。

为了让整体的逻辑简单,我使用了一个队列,当HTTP请求回调执行时,将结果存放在队列中,在主线程的Frame里面去处理队列中的信息,通过加锁的方式保证队列是线程安全的。

这是一个很基本的生产者-消费者线程模型,HTTP线程提供数据,主线程使用数据,而问题出在锁上。

引擎底层的线程库只提供了最基本的旋转锁,实际只是对InterlockedCompareExchange函数简单的封装了一层。

InterlockedCompareExchange函数一共有三个参数,Destination、Exchange和Comperand,Destination是需要操作的数,函数比较Destination和Comperand的值,如果两者相等则将Destination的值设为Exchange,否则不做动作。这里需要注意的是返回值,无论函数执行过程中是否发生了交换,函数最终都会返回操作前Destination的值。

所以封装的lock函数如下

void Lock()
{
    while( InterlockedCompareExchange(&mLocker, 1, 0) != 0 )
    {
        
    }
}

由于主线程中的读取是在Frame里的,我不希望用一个Lock去堵塞整个线程,于是我自己写了一个TryLock函数。

正确的TryLock函数如下

bool TryLock()
{
    return InterlockedCompareExchange( &mLocker, 1, 0 ) == 0;
}

整个模块的代码流程如下

//Main thread
void Frame()
{
    if(TryLock())
    {
        //read and process queue data
        Unlock();
    }
}

//HTTP thread
void Callback()
{
    Lock();
    //Enqueue data
    Unlock();
}

如果按照这个代码,程序应该是没问题的,但是我在写TryLock函数时,判断错了返回值,写成了如下的形式

扫描二维码关注公众号,回复: 5072214 查看本文章
bool TryLock()
{
    return InterlockedCompareExchange( &mLocker, 1, 0 ) != 0;
}

此时在Frame函数中,第一次执行TryLock,mLocker的值是0,执行之后mLocker的值变为1,加锁,但是由于返回值错误,导致TryLock的返回值为False,所以并不会执行Unlock,整个数据结构就被主线程锁住了,直到下一次Frame,TryLock返回True,数据结构解锁。

也就是说,每两帧中,只有一帧HTTP线程能够访问这个数据结构。

在多核处理器中,这个问题是不会有太明显的表现,因为两个线程可以在不同的处理器中并行,HTTP线程的死锁并不会影响到主线程,这也就是为什么QA没有发现问题。

问题出现在AMD的3核处理器上。游戏本身是锁核的,对2核以上的处理器,游戏会指定使用几个处理器核心,不会占用所有的核心。2核处理器上,主线程和HTTP线程位于不同核心,而3核处理器的情况下,游戏会完全运行到1个核心上(可能锁核的机制根本没有考虑过三核的情况。。。),HTTP中的死锁完全占用了整个核心的资源,所以游戏就完全卡住了。

最后找到了问题,修复了问题,然后下一步是发patch...ORZ

总结一下这件事情:

  1. 没事不要自己瞎造轮子。实际上有封装更完善的锁,也有不会占用整个核心资源的锁,但是我偏偏做了一个最坏的选择,使用了一个最底层最原始的锁。
  2. 过早的优化是造成bug的一大原因。之所以不用更复杂的锁,是因为他们都没有TryLock功能,TryLock初衷是为了防止主线程被锁阻塞(实际上如果锁是正确的这种情况发生的可能性几乎为0),完全可以不需要TryLock,直接使用任何的Lock都可以避免这个bug的发生。
  3. 涉及到多线程和锁的时候,代码一定要慎之又慎。因为这个bug两个同事查了三天(主要是太难重现了,因为公司没有三核的机器,最后通过虚拟机模拟了三核的机器才找出了bug)。

猜你喜欢

转载自blog.csdn.net/Zealot_Alie/article/details/81138641