当 运行 与立即 Window 时,lock() 块中的 ++ 是否有不同的应用?
Does a ++ inside a lock() block get applied differently when run from the Immediate Window?
我今天试图调试我们应用程序中重复 ID 的问题,我注意到来自即时 Window 的 运行ning 代码没有按预期运行。这是我用来测试的示例程序:
class Program
{
static void Main(string[] args)
{
ClassA a = new ClassA();
ClassB b = new ClassB();
Console.WriteLine(string.Format("A: {0}", a.Id));
Console.WriteLine(string.Format("B: {0}", b.Id));
Console.ReadLine();
}
}
public class ClassA
{
private static int _id = MySingleton.Instance.GetId(typeof(ClassA));
public int Id { get { return _id; } }
}
public class ClassB
{
private static int _id = MySingleton.Instance.GetId(typeof(ClassB));
public int Id { get { return _id; } }
}
public class MySingleton
{
private int _id = 0;
private object lockObj = new object();
Dictionary<string, int> _cache = new Dictionary<string, int>();
private static readonly MySingleton _mySingleton = new MySingleton();
public int GetId(Type t)
{
if (_cache.ContainsKey(t.FullName))
{
return _cache[t.FullName];
}
else
{
lock (lockObj)
{
Add(t.FullName, _id++);
return _id;
}
}
}
public void Add(string key, int value)
{
if (!_cache.ContainsKey(key))
_cache.Add(key, value);
}
public static MySingleton Instance
{
get { return _mySingleton; }
}
}
如果我运行这段代码,输出是
A: 1
B: 2
但是如果我在 static int _id
下一个断点并在立即 window 中检查 MySingleton.Instance.GetId(typeof(ClassA))
的值,那么它会显示第一个 1
的值时间,put 坚持 0
,所以输出是:
A: 0
B: 2
如果我将断点放在 ClassB 中并将 运行 MySingleton.Instance.GetId(typeof(ClassA))
放在立即 window 中,它第一次显示 2
的值,但仍然存在1
对于任何后续调用:
A: 1
B: 1
我实际期望的是,因为我使用的是 _id++
而不是 ++_id
,所以输出应该是
A: 0
B: 1
为什么会这样?
首先观察几点:
当您第一次调用 GetID
时,该类型的缓存不会被填充。该值被添加到缓存中,然后增加。但是返回的是增加的(我假设不正确)值(1
for ClassA
),而不是缓存中的值,它较低。因此,如果您为同一类型第二次调用 GetID
,您会得到一个低一个的值(0
对应 ClassA
)。
当您使用调试器并观察结果时,那些 "watches" 可能会修改结果。在您的情况下:手表 window 显示不正确的值(1
代表 ClassA
),之后程序使用正确的值(0
代表 ClassA
).
尝试像这样修复您的代码:
lock (lockObj)
{
Add(t.FullName, _id); // <-- remove the ++
return _id++; // <-- add the ++
}
这样,_id 的原始值在递增之前返回。
另一个观察:
- 您正在使用
Dictionay<K,V>
。这不是线程安全的。您不仅应该在写操作期间使用锁来保护它,而且在读操作期间也应该使用锁来保护它。使用您现在的代码,写操作期间读取可能会导致不良结果。考虑使用 ConcurrentDictionary<K,V>
.
我今天试图调试我们应用程序中重复 ID 的问题,我注意到来自即时 Window 的 运行ning 代码没有按预期运行。这是我用来测试的示例程序:
class Program
{
static void Main(string[] args)
{
ClassA a = new ClassA();
ClassB b = new ClassB();
Console.WriteLine(string.Format("A: {0}", a.Id));
Console.WriteLine(string.Format("B: {0}", b.Id));
Console.ReadLine();
}
}
public class ClassA
{
private static int _id = MySingleton.Instance.GetId(typeof(ClassA));
public int Id { get { return _id; } }
}
public class ClassB
{
private static int _id = MySingleton.Instance.GetId(typeof(ClassB));
public int Id { get { return _id; } }
}
public class MySingleton
{
private int _id = 0;
private object lockObj = new object();
Dictionary<string, int> _cache = new Dictionary<string, int>();
private static readonly MySingleton _mySingleton = new MySingleton();
public int GetId(Type t)
{
if (_cache.ContainsKey(t.FullName))
{
return _cache[t.FullName];
}
else
{
lock (lockObj)
{
Add(t.FullName, _id++);
return _id;
}
}
}
public void Add(string key, int value)
{
if (!_cache.ContainsKey(key))
_cache.Add(key, value);
}
public static MySingleton Instance
{
get { return _mySingleton; }
}
}
如果我运行这段代码,输出是
A: 1 B: 2
但是如果我在 static int _id
下一个断点并在立即 window 中检查 MySingleton.Instance.GetId(typeof(ClassA))
的值,那么它会显示第一个 1
的值时间,put 坚持 0
,所以输出是:
A: 0 B: 2
如果我将断点放在 ClassB 中并将 运行 MySingleton.Instance.GetId(typeof(ClassA))
放在立即 window 中,它第一次显示 2
的值,但仍然存在1
对于任何后续调用:
A: 1 B: 1
我实际期望的是,因为我使用的是 _id++
而不是 ++_id
,所以输出应该是
A: 0 B: 1
为什么会这样?
首先观察几点:
当您第一次调用
GetID
时,该类型的缓存不会被填充。该值被添加到缓存中,然后增加。但是返回的是增加的(我假设不正确)值(1
forClassA
),而不是缓存中的值,它较低。因此,如果您为同一类型第二次调用GetID
,您会得到一个低一个的值(0
对应ClassA
)。当您使用调试器并观察结果时,那些 "watches" 可能会修改结果。在您的情况下:手表 window 显示不正确的值(
1
代表ClassA
),之后程序使用正确的值(0
代表ClassA
).
尝试像这样修复您的代码:
lock (lockObj)
{
Add(t.FullName, _id); // <-- remove the ++
return _id++; // <-- add the ++
}
这样,_id 的原始值在递增之前返回。
另一个观察:
- 您正在使用
Dictionay<K,V>
。这不是线程安全的。您不仅应该在写操作期间使用锁来保护它,而且在读操作期间也应该使用锁来保护它。使用您现在的代码,写操作期间读取可能会导致不良结果。考虑使用ConcurrentDictionary<K,V>
.