【问题标题】:CSingleLock implementation works ok on Windows 7 but ends in a deadlock on Win XPCSingleLock 实现在 Windows 7 上运行良好,但在 Win XP 上以死锁结束
【发布时间】:2013-10-16 16:34:41
【问题描述】:

我尝试用我自己的替换 MFC 的 CSingleLock 实现,但我现在在 Windows XP 上遇到了死锁,而我在 Windows 7 上没有这种死锁,而且我在任何带有 MFC 的 CSingleLock 的操作系统上都没有,除了查看每个在我的应用中锁定和解锁,我的实现中缺少什么:

class CCriticalSection
{
    CRITICAL_SECTION m_cs; 
public:
    CCriticalSection()
    {
        InitializeCriticalSection(&m_cs);
    }
    ~CCriticalSection()
    {
        DeleteCriticalSection(&m_cs);
    }
    void Lock()
    {   
        EnterCriticalSection(&m_cs);
    }
    BOOL TryLock()
    {
        return TryEnterCriticalSection(&m_cs);
    }
    void Unlock()
    {
         if(m_cs.LockCount > -1)
              LeaveCriticalSection(&m_cs);
    }
};

#include "CCriticalSection.h"
class CSingleLock {
    CCriticalSection *m_cs;
public:
    CSingleLock(CCriticalSection* cs = NULL, bool bLock = false)
    {
        m_cs = cs;
        if(m_cs != NULL)
        {
            if(bLock)
                m_cs->Lock();
        }
    }
    void Unlock()
    {
        if(m_cs != NULL)
            m_cs->Unlock();
    }
    void Lock()
    {
        if(m_cs != NULL)
            m_cs->Lock();
    }
    ~CSingleLock()
    {
        if(m_cs != NULL)
            m_cs->Unlock();
    }

};

【问题讨论】:

  • 1 有什么理由不使用 ATL/MFC 类 CComAutoCriticalSection 和朋友? 2 第二类是不准确的,例如它可能会发生你解锁而不锁定。方便的助手类不是这样设计的。
  • @RomanR。如果我从事非商业项目,我会避免使用 ATL/MFC,因为它们不附带 VS Express。 (反正他们从来没有用过,我还没有在 VS 2012 中检查过。)
  • @arx:这个问题被标记为mfc,而发帖人过去曾向atl提问。
  • 因为我被要求从使用它们的库中获取 MFC/ATL ......我已经这样做了,在 Windows 7 上工作时,现在我遇到了这个问题在 XP 上测试时。
  • 您没有发布导致问题的代码。上面的类并不健壮,如果使用不当,它们可能会导致问题。您需要查看更高级别的代码。一个典型的问题是解锁从不锁定的部分,并删除锁定的部分。一个更好的班级会得到asserts 来报告这个。

标签: windows multithreading winapi visual-c++ mfc


【解决方案1】:

要使锁定类真正有用,您需要像这样改进它:

class CSingleLock 
{
    CCriticalSection *m_cs;
    bool m_bLock;

public:
    CSingleLock(CCriticalSection* cs = NULL, bool bLock = false)
    {
        m_cs = cs;
        if(m_cs != NULL)
        {
            if(bLock)
                m_cs->Lock();
            m_bLock = bLock;
        }
    }
    void Unlock()
    {
        if(!m_cs || !m_bLock)
            return;
        m_cs->Unlock();
        m_bLock = false;
    }
    void Lock()
    {
        if(!m_cs || m_bLock)
            return;
        m_cs->Lock();
        m_bLock = true;
    }
    ~CSingleLock()
    {
        Unlock();
    }
};

它应该足够智能,让你远离错误使用 CS 的麻烦。

【讨论】:

  • 请注意,如果 cs 为 NULL,则构造函数未初始化 m_bLock
  • @JonathanPotter:!m_csm_bLock 永远不会被访问。
  • @RomanR。除非,例如,用户添加了 Set() 方法,该方法在构造后设置临界区,而忽略初始化标志。
  • @JonathanPotter:你是对的,当然,我的中风是用最小的努力添加锁检查。如果让我选择,m_cs 肯定是CCriticalSection&
【解决方案2】:

MSDN 说“如果一个线程调用LeaveCriticalSection 而它没有指定临界区对象的所有权,则会发生错误,这可能会导致另一个使用EnterCriticalSection 的线程无限期地等待。”。

CSingleLock 的析构函数调用CriticalSection::Unlock,后者调用LeaveCriticalSection,但没有检查关键部分是否由当前线程持有。

您需要跟踪关键部分的所有权。

【讨论】:

  • 为什么我在 windows 7 上没有遇到死锁?
  • 我确实遇到了您在回答中描述的情况.. 但我需要 CSingleLock 对象可以从多个线程访问,因为它会阻止其他可能的线程访问公共数组结构
  • 也许它确实死锁了,而您只是还没有看到它,或者它以更有害的方式失败(例如,它不保护共享资源)。没关系,代码被破坏了。更有建设性的是,如果 CSingleLock 是共享的,为什么析构函数调用 Unlock?
  • @A.K "发生可能导致另一个线程无限期等待的错误。它表示挂起可能发生,而不是发生。离开您不拥有的关键部分会导致未定义的行为,因此任何事情都可能发生。看起来工作是未定义行为的一种有效形式。所以看起来工作了 5 分钟,然后神秘地崩溃了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-15
  • 2011-03-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多