我有一个可以被多种原因锁定的系统 这是静态类,负责保持锁定状态:
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
类是静态的吗?
答案 0 :(得分:6)
是的,你这里有竞争条件;不,这不是因为班级是static
。
问题是在LockedByReason1 = false
和Locker.Locked
的读取之间(它本身远,远与原子操作相比),其他线程可能会执行使您的代码锁定条件为真。
一般来说,这种设计根本不提供对竞争条件的任何保护,因为它既不使用原子操作也不使用同步原语(例如lock
语句)。
答案 1 :(得分:0)
你没有任何竞争条件,因为没有种族。这只是顺序单线程操作。您将所有LockedByReason值设置为false(bool字段为false加默认值,您还将LockedByReason1设置为false)。在条件语句中,检查Locked值,这给出了第一个日志记录。结果是错误的,这导致第二个,#34; Not unlocking system"日志记录。日志记录的相同时间是因为输出比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;
}
}
}