为什么会尝试/终于,而不是“使用”的声明有助于避免出现竞争状况?(Why would try/fin

2019-07-19 18:33发布

这个问题涉及到在另一张贴在这里评论: 取消实体框架查询

我将重现代码示例从那里净度:

    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();

我不能作出这样的表示注释的意义

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

entities是一个局部变量,如果代码被另一个线程上重新输入不会被共享,并在同一个线程内,它似乎完全安全的(实际上等同于给定的代码),以分配给它里面的“使用”的声明以通常的方式,而不是人工做的事情与尝试/最后。 任何人都可以告诉我吗?

Answer 1:

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

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

至:

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

当线程obj的赋值语句和try块之间的权利中止发生的种族。 极小的可能性,但不为零。 当这种情况发生的对象将不被设置。 注意他是如何通过移动try块内的分配,以便就不会发生这场比赛改写了代码。 实际上没有去当比赛发生根本性的错误,处置对象不是必需的。

拥有了发展的线程之间进行选择中止略微更加高效和using语句手工编写,应首先选择使用Thread.Abort的习惯没有得到()。 我不能建议其实这样做, 使用语句具有额外的安全措施,确保不出意外,它可以确保即使在对象using语句内重新分配原来的对象被布置。 添加catch子句是不太容易发生事故为好。 using语句存在,以减少错误的可能性,可以经常使用。


Noodling上一些关于这个问题,答案是受欢迎的,还有来自完全相同的比赛中遭遇另一种常见的C#声明。 它看起来像这样:

lock (obj) {
    // statements
}

翻译为:

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

完全相同的情况下,线程中止可确定()调用之后和进入try块前罢工。 其阻止作出的退出()调用。 这是比不发过程的Dispose()方法调用的方式厉害,这几乎肯定会导致死锁。 问题是特定于64位抖动,肮脏的细节在这个很好的描述乔·达菲的博客文章 。

这是很难可靠地解决这个问题之一,移动try块内的输入()调用解决不了的问题。 你不能肯定的是,进入有人呼吁,所以你不能可靠地调用exit()方法没有可能引发的异常。 这达菲在谈论并最终会发生的Monitor.ReliableEnter()方法。 监视器的.NET 4的版本有一个TryEnter()重载需要一个ref bool lockTaken说法。 现在你知道它是好的,调用exit()。

那么,可怕的东西,去BUMP在晚上,当你不看。 编写代码是安全可中断是很难的。 你会是明智的,不要假定代码,你不写了这一切照顾的。 测试这样的代码是非常困难的,因为比赛是如此罕见。 你永远无法确定。



Answer 2:

很奇怪,会导致using仅仅是语法糖尝试- finally块。

从MSDN :

您可以通过将对象try块内,然后调用Dispose在finally块达到同样的效果; 其实,这是怎么用的语句是由编译器翻译。



Answer 3:

根据您是否使用using或明确地try/finally可以使用示例代码,你将有略有不同的代码

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

代与using语句它可能看起来像

   using(var entities = new AdventureWorks2008R2Entities()){
      ...
   }

这accordig不规范的§8.13将扩大到

    AdventureWorks2008R2Entities entities = new AdventureWorks2008R2Entities();
    try
    {
        ...
    } finally {
    } 

为此唯一的真正的区别在于分配是不是在try / finally块之内,但有没有结果中,可能发生竞争条件(除线程终止其间的assignement和try块为汉斯还指出)



Answer 4:

该意见没有任何意义,因为using语句将被转换到一个try /终于由编译器模块。 由于“实体”将不会在范围外使用,它更容易使用使用statment因为这会自动配置资源。

你可以阅读更多有关这在MSDN: using语句(C#参考)



文章来源: Why would try/finally rather than a “using” statement help avoid a race condition?