我需要使用来自第三方API的属性getter,只需访问该getter 有时挂起整个应用程序,有时有效(在dev / debugger中) 。如果我将它部署到生产服务器,主机应用程序有时按预期工作,有时在KERNEL32中死于APPCRASH。
我无法理解一个简单的getter如何冻结或崩溃任何东西,所以我可能这个属性是伪装的方法,并使用我最喜欢的反编译器来查找。
这是我遇到的计划/模式:
internal MyClass() : ISomeInterface
{
Locker = new object();
}
public object Locker { get; private set; }
private SomeObject _myProperty;
public SomeObject MyProperty
{
get
{
lock (Locker)
{
if (_myProperty== null)
MyProperty = SomeCondition ? SomeMethod() : Something.Else;
return _myProperty;
}
}
private set
{
_myProperty = value;
}
}
我是否正确地相信,只要_myProperty
为null
,getter就会调用setter,并且此处存在竞争条件错误,可能导致随机/偶然冻结我正在体验?如何确认/确认此冻结实际上是死锁?
什么是正确的实施?我被引导相信如果setter也有一个lock (Locker)
(我不喜欢Locker
可以公开访问,听起来像是在寻找麻烦),而getter会分配{{1 <},而不是调用setter,它是线程安全的。
最后,有没有一种方法可以调用此getter而无需第三方发送“固定”版本,而且不会冻结?
我试过从主UI线程调用它,我试过从_myProperty
/后台线程调用它,我在调用之前尝试了Task
,我尝试设置超时Sleep(200)
...无论我做什么,我似乎都无法始终如一地调用getter并可靠地获得预期结果( 工作......有时候)
Task
对象是公共的,但它是在Locker
中定义的,我只能通过它的界面访问它,当然这不会暴露它。有没有办法可以使用反射从外部获取锁定(internal class
)并实际让它起作用?或者那个吸气剂完全是borked?
答案 0 :(得分:5)
我是否正确相信每当_myProperty为null时,getter会调用setter并且这里存在一个竞争条件错误,可能导致我遇到的随机/偶然冻结?我怎样才能确认/证实这种冻结实际上是一个僵局?
没有。在C#中,lock
可重入同一个线程。您可以在Monitor.Enter的文档中看到这一点:
同一个线程在没有阻塞的情况下多次调用Enter是合法的;但是,在等待对象的其他线程将解除阻塞之前,必须调用相同数量的Exit调用。
不喜欢Locker可公开访问,听起来像是在寻找麻烦
我同意你的意见。这更可能是罪魁祸首。 Locker
最公开的事实意味着库中的其他内容锁定了此对象,可能导致死锁。
如果您可以通过Visual Studio Concurrency Profiler运行它,您应该能够在死锁处暂停,并在该时间点看到阻塞的对象。
答案 1 :(得分:2)
有没有办法可以使用反射从外部获取锁(Monitor.TryEnter(magicallyObtainedLocker))并实际让它工作?
是的,鉴于你可以反编译它,我猜是这样的 1 ;以下“works on my machine”:
1 我猜你可以从外面获得锁定;希望但不能预测这是否足以避免僵局。
using System;
namespace ThirdParty
{
public interface ISomeInterface
{
string MyProperty { get; }
}
public static class Factory
{
public static ISomeInterface create() { return new MyClass(); }
}
internal class MyClass : ISomeInterface
{
internal MyClass()
{
Locker = new object();
}
public object Locker { get; private set; }
private string _myProperty;
public string MyProperty
{
get
{
lock (Locker)
{
if (_myProperty == null)
MyProperty = "foo";
return _myProperty;
}
}
private set
{
_myProperty = value;
}
}
}
}
using System;
using System.Linq;
using System.Reflection;
using System.Threading;
using ThirdParty;
namespace InternalLocker
{
class Program
{
static void Main(string[] args)
{
test1();
test2();
}
static void test1()
{
ISomeInterface thing = Factory.create();
string foo = thing.MyProperty;
assert(foo == "foo");
}
static void test2()
{
ISomeInterface thing = Factory.create();
// use reflection to find the public property
Type type = thing.GetType();
PropertyInfo propertyInfo = type.GetProperty("Locker");
// get the property value
object locker = propertyInfo.GetValue(thing, null);
// use the property value
if (Monitor.TryEnter(locker))
{
string foo = thing.MyProperty;
Monitor.Exit(locker);
assert(foo == "foo");
}
else
assert(false);
}
static void assert(bool b)
{
if (!b)
throw new Exception();
}
}
}