1

我正在查看 .NET 4.0 的 System.Threading.Tasks.TaskScheduler.Id 实现,并查看以下代码:

[__DynamicallyInvokable]
public int Id
{
    [__DynamicallyInvokable]
    get
    {
        if (this.m_taskSchedulerId == 0)
        {
            int num = 0;
            do
            {
                num = Interlocked.Increment(ref s_taskSchedulerIdCounter);
            }
            while (num == 0);
            Interlocked.CompareExchange(ref this.m_taskSchedulerId, num, 0);
        }
        return this.m_taskSchedulerId;
    }
}

为什么msft在联锁后比较num==0?Interlocked.Increment()的实现表示它返回递增的值(在递增之后),因此检查零似乎是不必要的(除非您的计数器环绕,但如果发生这种情况,您会有更大的问题在这里也没有解决。

如果我要这样做,我会这样做:

public int Id
        {
            get
            {
                if(m_taskSchedulerId==0)
                {
                    var result = Interlocked.Increment(ref s_taskSchedulerIdCounter);
                    Interlocked.CompareExchange(ref m_taskSchedulerId, result, 0);
                }
                return m_taskSchedulerId;
            }
        }
4

2 回答 2

8

但如果发生这种情况,你会有更大的问题

不,这就是他们这样做的确切原因。从参考来源

public Int32 Id
{
    get
    {
        if (m_taskSchedulerId == 0)
        {
            int newId = 0;

            // We need to repeat if Interlocked.Increment wraps around and returns 0.
            // Otherwise next time this scheduler's Id is queried it will get a new value
            do
            {
                newId = Interlocked.Increment(ref s_taskSchedulerIdCounter);
            } while (newId == 0);

            Interlocked.CompareExchange(ref m_taskSchedulerId, newId, 0);
        }

        return m_taskSchedulerId;
    }
}
于 2012-09-28T07:06:27.050 回答
1

不变量是Id永远不会返回 0,因为 0 用于内部用于指示值应该被初始化。如果Id曾经是 0,那么下次您查询它时,它将是一个不同的值。

于 2012-09-28T07:08:18.880 回答