gpt4 book ai didi

c# - 为什么 try/finally 而不是 "using"语句有助于避免竞争条件?

转载 作者:IT王子 更新时间:2023-10-29 04:23:20 24 4
gpt4 key购买 nike

此问题与此处另一篇文章中的评论相关:Cancelling an Entity Framework Query

为了清楚起见,我将从那里重现代码示例:

    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

entities 是一个局部变量,如果代码在另一个线程上重新输入,则不会共享,并且在同一个线程中它看起来非常安全(并且实际上等同于给定的代码) 以通常的方式将其分配到“using”语句中,而不是使用 try/finally 手动执行操作。谁能赐教一下?

最佳答案

是的,using 语句中可能存在竞争。 C# 编译器转换

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

到:

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

当线程在 obj 赋值语句和 try block 之间中止时,就会发生竞争。赔率极小但不为零。发生这种情况时,该对象不会被处置。请注意他是如何通过将赋值移动到 try block 中来重写该代码的,这样就不会发生这种竞争。当竞争发生时,实际上没有什么根本性的错误,不需要处理对象。

必须在稍微提高线程中止效率和手动编写 using 语句之间做出选择,您应该首先选择不要养成使用 Thread.Abort() 的习惯。我不建议实际这样做,using 语句有额外的安全措施来确保事故不会发生,即使在使用语句。添加 catch 子句也不太容易发生事故。 using 语句的存在是为了减少出现错误的可能性,请始终使用它。


关于这个问题的一点点思考,答案很受欢迎,还有另一个常见的 C# 语句遭受完全相同的竞争。它看起来像这样:

lock (obj) {
// statements
}

翻译成:

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

完全相同的场景,线程中止可以在 Enter() 调用之后和进入 try block 之前触发。这阻止了 Exit() 调用。这 方式 比没有进行的 Dispose() 调用更糟糕,当然,这几乎肯定会导致死锁。该问题特定于 x64 抖动,此 Joe Duffy blog post 中详细描述了肮脏的细节.

很难可靠地修复这个问题,将 Enter() 调用移动到 try block 中无法解决问题。您无法确定是否进行了 Enter 调用,因此您无法在不触发异常的情况下可靠地调用 Exit() 方法。 Duffy 所说的 Monitor.ReliableEnter() 方法最终实现了。 Monitor 的 .NET 4 版本有一个 TryEnter() 重载,它带有一个 ref bool lockTaken 参数。现在您知道可以调用 Exit() 了。

好吧,可怕的东西会在你不注意的时候突然出现。编写可安全中断的代码是困难。明智的做法是永远不要假设不是您编写的代码已经处理了所有这些问题。测试此类代码非常困难,因为比赛非常罕见。你永远无法确定。

关于c# - 为什么 try/finally 而不是 "using"语句有助于避免竞争条件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14830534/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com