繁体   English   中英

静态类的奇怪行为

[英]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 = falseLockedByReason1 = 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM