[英]Strange behavior of a static class
我有一个由于多种原因可以锁定的系统。
这是静态类,负责保持锁定状态:
internal static class Locker
{
private static ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
internal static bool LockedByReason1 { get; set; }
internal static bool LockedByReason2 { get; set; }
internal static bool LockedByReason3 { get; set; }
internal static bool Locked
{
get
{
log.DebugFormat("LockedByReason1: {0}, LockedByReason2: {1}, LockedByReason3: {2}", LockedByReason1, LockedByReason2, LockedByReason3);
return LockedByReason1 || LockedByReason2 || LockedByReason3;
}
}
}
这是业务逻辑中的代码:
Locker.LockedByReason1 = false;
if (Locker.Locked)
log.Info("Unlocking system...");
else
log.Info("Not unlocking system");
我的日志文件显示以下文本:
2014-06-06 10:54:31,765 DEBUG Client.Utils.Locker - LockedByReason1: False, LockedByReason2: False, LockedByReason3: False
2014-06-06 10:54:31,765 INFO Client.BusinessLogicManager - Not unlocking system
如您所见,同时调用了LockedByReason1
属性的设置和查询Locked
状态。
我这里有比赛条件问题吗?
是因为Locker
类是静态的吗?
是的,您这里有比赛条件; 不,不是因为该类是static
。
问题在于,在LockedByReason1 = false
和LockedByReason1 = false
的读取Locker.Locked
(这本身距离原子操作很远 ),其他线程可能会执行使锁定条件成立的代码。
通常,此设计根本不提供任何针对竞争条件的保护,因为它既不使用原子操作也不使用同步原语(例如lock
语句)。
您在这里没有任何比赛条件,因为没有比赛。 这只是顺序的单线程操作。 您已将所有LockedByReason值设置为false(布尔字段为false加上默认值,并且还将LockedByReason1设置为false)。 在条件语句中,您检查Locked值,这给出了第一个日志记录。 结果为假,这将导致第二个“未解锁系统”日志记录。 日志中的相同时间记录是因为输出比DateTime精度快得多。
但是您的代码不是线程安全的。 因此,在不使用昂贵的“锁定”指令的情况下,经过了一些优化的版本可能看起来像:
internal static class Locker
{
private static ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
// We join or lockers into one field, because boolean cann't be handled by Interlocked routines.
private static volatile int _Lockers = 0;
// Thread-safe update routine
internal static void AtomicUpdate(int mask, bool value)
{
SpinWait sw = new SpinWait();
do
{
int old = _Lockers;
if (Interlocked.CompareExchange(ref _Lockers, value ? old | mask : old &~mask, old) == old)
{
return;
}
sw.SpinOnce();
} while (true);
}
// Reason 1 will be the first bit of _Lockers field, Reason 2 - the second and so on.
internal static bool LockedByReason1
{
get
{
return (_Lockers & 1) > 0;
}
set
{
Locker.AtomicUpdate(1, value);
}
}
internal static bool LockedByReason2
{
get
{
return (_Lockers & 2) > 0;
}
set
{
Locker.AtomicUpdate(2, value);
}
}
internal static bool LockedByReason3
{
get
{
return (_Lockers & 4) > 0;
}
set
{
Locker.AtomicUpdate(4, value);
}
}
internal static bool Locked
{
get
{
log.DebugFormat("LockedByReason1: {0}, LockedByReason2: {1}, LockedByReason3: {2}", LockedByReason1, LockedByReason2, LockedByReason3);
return _Lockers > 0;
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.