为什么会尝试/终于而不是“使用”声明帮助避免竞争条件? [英] Why would try/finally rather than a "using" statement help avoid a race condition?

查看:93
本文介绍了为什么会尝试/终于而不是“使用”声明帮助避免竞争条件?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

此问题涉及另一篇文章中的评论:取消实体框架查询

This question relates to a comment in another posting here: Cancelling an Entity Framework Query

我将从中重现代码示例:

I will reproduce the code example from there for clarity:

    var thread = new Thread((param) =>
    {
        var currentString = param as string;

        if (currentString == null)
        {
            // TODO OMG exception
            throw new Exception();
        }

        AdventureWorks2008R2Entities entities = null;
        try // Don't use using because it can cause race condition
        {
            entities = new AdventureWorks2008R2Entities();

            ObjectQuery<Person> query = entities.People
                .Include("Password")
                .Include("PersonPhone")
                .Include("EmailAddress")
                .Include("BusinessEntity")
                .Include("BusinessEntityContact");
            // Improves performance of readonly query where
            // objects do not have to be tracked by context
            // Edit: But it doesn't work for this query because of includes
            // query.MergeOption = MergeOption.NoTracking;

            foreach (var record in query 
                .Where(p => p.LastName.StartsWith(currentString)))
            {
                // TODO fill some buffer and invoke UI update
            }
        }
        finally
        {
            if (entities != null)
            {
                entities.Dispose();
            }
        }
    });

thread.Start("P");
// Just for test
Thread.Sleep(500);
thread.Abort();

我无法理解评论说


不要使用,因为它会导致竞争条件

Don't use using because it can cause race condition

实体是一个局部变量,如果代码在另一个线程上重新输入,并且在同一个线程中,它将不会被共享,这似乎是完全安全的(并且实际上等同于给定的代码)以通常的方式将其分配给using语句,而不是用try / finally手动进行操作。任何人都可以启发我吗?

entities is a local variable and won't be shared if the code is re-entered on another thread, and within the same thread it would seem perfectly safe (and indeed equivalent to the given code) to assign it inside a "using" statement in the usual way, rather than doing things manually with the try/finally. Can anyone enlighten me?

推荐答案

是的,使用语句有可能的比赛。 C#编译器转换

Yeah, there is a possible race in the using statement. The C# compiler transforms

using (var obj = new Foo()) {
    // statements
}

to:

var obj = new Foo();
try {
   // statements
}
finally {
   if (obj != null) obj.Dispose();
}

比赛发生在线程在 obj 赋值语句和try块。非常小的赔率,但不是零。当这种情况发生时,对象不会被处理。注意他如何通过在try块中移动分配来重新编写代码,因此这种比赛不会发生。当比赛发生时,没有什么实质上是错误的,处理对象不是一个要求。

The race occurs when the thread is aborted right between the obj assignment statement and the try block. Extremely small odds but not zero. The object won't be disposed when that happens. Note how he rewrote that code by moving the assignment inside the try block so this race cannot occur. Nothing actually goes fundamentally wrong when the race occurs, disposing objects is not a requirement.

不得不选择使线程中止更有效率,并使用语句,您应该首先选择不习惯使用Thread.Abort()的习惯。我不能建议实际上这样做,使用语句具有额外的安全措施,以确保事故不发生,它确保原始对象即使在对象重新分配使用声明。添加catch子句也不太容易发生事故。 使用语句存在,以减少错误的可能性,始终使用它。

Having to choose between making thread aborts marginally more efficient and writing using statements by hand, you should first opt for not getting in the habit of using Thread.Abort(). I can't recommend actually doing this, the using statement has additional safety measures to ensure accidents don't happen, it makes sure that the original object gets disposed even when the object is re-assigned inside the using statement. Adding catch clauses is less prone to accidents as well. The using statement exists to reduce the likelihood of bugs, always use it.

对这个问题一点点讨厌,答案很受欢迎,还有一个常见的C#语句,与同样的比赛。看起来像这样:

Noodling on a bit about this problem, the answer is popular, there's another common C# statement that suffers from the exact same race. It looks like this:

lock (obj) {
    // statements
}

翻译为:

Monitor.Enter(obj);
// <=== Eeeek!
try {
    // statements
}
finally {
    Monitor.Exit(obj);
}

完全相同的情况下,线程中止可以在Enter()调用后发生,在进入尝试块之前。这阻止了Exit()调用。这是一种比当然不是Dispose()调用的方式,这几乎肯定会导致死锁。这个问题是针对x64抖动的,这个细节在这个 Joe Duffy博客

Exact same scenario, the thread abort can strike after the Enter() call and before entering the try block. Which prevents the Exit() call from being made. This is way nastier than a Dispose() call that isn't made of course, this is almost certainly going to cause deadlock. The problem is specific to the x64 jitter, the sordid details are described well in this Joe Duffy blog post.

很难可靠地修复这个,移动try块内的Enter()调用无法解决问题。您无法确定进入的呼叫是否已经进行,因此您无法可靠地调用Exit()方法而不会触发异常。达菲正在谈论的Monitor.ReliableEnter()方法最终发生了。 .NET 4版本的Monitor得到了一个TryEnter()重载,它接受一个 ref bool lockTaken 参数。现在你知道可以打电话给Exit()。

It is very hard to reliably fix this one, moving the Enter() call inside the try block can't solve the problem. You cannot be sure that the Enter call was made so you cannot reliably call the Exit() method without possibly triggering an exception. The Monitor.ReliableEnter() method that Duffy was talking about did eventually happen. The .NET 4 version of Monitor got a TryEnter() overload that takes a ref bool lockTaken argument. Now you know it is okay to call Exit().

那么当你不看的时候,可怕的东西在晚上BUMP。编写可安全中断的代码是 。你永远不会认为你没有写的代码是所有这一切的照顾。测试这样的代码是非常困难的,因为比赛是如此罕见。你永远不能确定。

Well, scary stuff that goes BUMP in the night when you are not looking. Writing code that's safely interruptable is hard. You'd be wise to never assume that code that you didn't write got all of this taken care of. Testing such code is extremely difficult since the race is so rare. You can never be sure.

这篇关于为什么会尝试/终于而不是“使用”声明帮助避免竞争条件?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆