C# 有没有什么方法能让我称之为“获得者”,并始终如一地获得预期的结果?
我需要使用来自第三方API的属性getter,仅访问该getter有时会挂起整个应用程序,有时会起作用(在dev/debugger中)。如果我将其部署到生产服务器,主机应用程序有时会按预期工作,有时会因内核32中的APPCRASH而死亡 我不明白一个简单的getter是如何冻结或崩溃任何东西的,所以我认为这个属性可能是一个伪装的方法,并使用我最喜欢的反编译器来发现 这就是我遇到的方案/模式:C# 有没有什么方法能让我称之为“获得者”,并始终如一地获得预期的结果?,c#,multithreading,locking,race-condition,C#,Multithreading,Locking,Race Condition,我需要使用来自第三方API的属性getter,仅访问该getter有时会挂起整个应用程序,有时会起作用(在dev/debugger中)。如果我将其部署到生产服务器,主机应用程序有时会按预期工作,有时会因内核32中的APPCRASH而死亡 我不明白一个简单的getter是如何冻结或崩溃任何东西的,所以我认为这个属性可能是一个伪装的方法,并使用我最喜欢的反编译器来发现 这就是我遇到的方案/模式: internal MyClass() : ISomeInterface { Locker = n
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;
}
}
我认为只要\u myProperty
为null
,getter就会调用setter,并且这里有一个争用条件错误,这可能会导致我遇到的随机/偶尔冻结,对吗?我如何确认/确认这种冻结实际上是一种僵局
什么是正确的实现?我相信如果setter也有一个锁(Locker)
(我也不喜欢锁
,听起来像是自找麻烦),getter会分配\u myProperty
,而不是调用setter,这将是线程安全的
最后,有没有一种方法可以称之为getter,而不需要第三方提供“固定”版本,也不需要冻结
我尝试过从主UI线程调用它,我尝试过从任务
/后台线程调用它,我尝试过睡眠(200)
在调用之前,我尝试过在任务
上设置超时。。。无论我做什么,我似乎都无法始终如一地称之为getter并可靠地获得预期的结果(有时它确实有效)
Locker
对象是公共的,但它是在内部类
中定义的,我只能通过它的接口访问它,当然不会公开它。是否有一种方法可以使用反射从外部获取锁(Monitor.TryEnter(magicallyObtainedLocker)
),并实际使其工作?或者说,这是一个完整的getter
我认为只要_myProperty为null,getter就会调用setter,并且这里有一个争用条件bug,这可能会导致我所经历的随机/偶然冻结,对吗?我如何确认/确认这种冻结实际上是一种僵局
否。在C#中,lock
对于同一线程是可重入的。您可以在以下文档中看到:
同一线程在没有阻塞的情况下多次调用Enter是合法的;但是,在等待对象的其他线程解除阻止之前,必须调用相同数量的退出调用
我也不喜欢这个储物柜可以公开使用,听起来像是自找麻烦 我同意你的看法。这更有可能是罪魁祸首。
Locker
是公共的这一事实很可能意味着库中的其他东西正在锁定此对象,这可能是死锁的原因
如果可以通过VisualStudio并发探查器运行此操作,则应该能够在死锁时暂停,并在该时间点看到对象阻塞
是否有一种方法可以使用反射从外部获取锁(Monitor.TryEnter(magicallyObtainedLocker))并实际使其工作
是的,考虑到你可以反编译它,我猜是so1;以下“”:
- “第三方”作为DLL构建在自己的独立项目中
- “InternalLocker”是另一个项目中的控制台程序,它引用第三方DLL
我没有答案给你,但我只想对那个能手/二传手组合说“恶心!”。那太…糟糕了!@retailcoder是一个受欢迎的用户,这是有原因的——他希望避免处理这样的代码!不幸的是,我认为你最好的选择是让第三方修复他们的库:(使用你最喜欢的反编译器来找出还有谁在使用这个储物柜!在
locker
上查找用法会产生很少的结果,所有的getter都是这样设置的,都在同一个类中;但是这个getter是唯一一个调用setter的getter。看起来是公开的,带有YAGNI标记…@retailcoder你得到了一个appcrash sugg它可能是SomeMethod
或getter中访问的某个属性中的一个问题,事实上……因此它与属性自身的运行和多线程处理无关?@retailcoder属性“自身的运行”很好。死锁可能会给出不同的行为,因为它只会导致无限挂起。。。
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();
}
}
}