Monitor.Exit()有时会挂起

时间:2016-04-19 07:10:50

标签: c# multithreading locking

使用Monitor.Enter和Monitor.Exit时,我似乎遇到了并发问题。有时我的代码会挂在以下Monitor.Exit语句中:

public void EndInit()
{
    Monitor.Enter(this.lockObj);
    this.initCount--;
    if (this.initCount == 0) {
        this.IsInitializing = false;
        this.IsInitialized = true;
        this.OnInitialized();
    }
    // sometimes, this Exit will never return ...
    Monitor.Exit(this.lockObj);
}

只有一个地方,我的 lockObj 使用了:

public void BeginInit()
{
    Monitor.Enter(this.lockObj);
    this.initCount++;
    this.IsInitializing = true;
    this.IsInitialized = false;
    Monitor.Exit(this.lockObj);
}

这就是我声明这个同步对象的方式:

private readonly object lockObj = new object();

我正在撕掉我的头发,发现它在这里发生了什么,但没有成功。我希望Monitor.Enter()能够阻止,直到我的同步对象被释放,但为什么Monitor.Exit()会被阻止?我无法在 MSDN 中找到有关此行为的任何解释。

注意我无法重现这种行为,它相当随机地发生(好吧,我知道“随机”不是正确的措辞)。

非常感谢任何想法或有用的提示!

和Thorsten

1 个答案:

答案 0 :(得分:3)

我从之前的评论中回答。因为当try finally中发生异常时,您应该使用Monitor.Exit构造正确地调用OnInitialize()

因此代码将成为

public void EndInit()
{
    Monitor.Enter(this.lockObj);
    try
    {
        this.initCount--;
        if (this.initCount == 0) {
            this.IsInitializing = false;
            this.IsInitialized = true;
            this.OnInitialized();
        }
    } 
    finally
    {
        Monitor.Exit(this.lockObj);    
    }
}

第二种方法也是如此。

它也可以这样编写

public void EndInit()
{
    lock(this.lockObj)
    {
        this.initCount--;
        if (this.initCount == 0) {
            this.IsInitializing = false;
            this.IsInitialized = true;
            this.OnInitialized();
        }
    }
}

修改

可以找到Joe Albahari撰写的关于线程的非常好的解释here。这是当之无愧的阅读。

编辑2(完整性)

如Damien_The_Unbeliever所述,还有overload

这只适用于.NET 4及更高版本。使用监视器的代码将变为:

public void EndInit()
{
    bool lockAcuired = false;
    try
    {
        Monitor.Enter(this.lockObj, ref lockAquired);
        this.initCount--;
        if (this.initCount == 0) {
            this.IsInitializing = false;
            this.IsInitialized = true;
            this.OnInitialized();
        }
    } 
    finally
    {
        if(lockAquired)
            Monitor.Exit(this.lockObj);    
    }
}