Why lock is needed to implement a readonly int property?

為{幸葍}努か 提交于 2019-12-08 00:44:13

问题


I'm new to threading and I came accross a custom thread pool implementation example in a blog. I'm pasting just the necessary parts of the code:

Public Class ThreadPool

    Private CountLock As New Object
    Private _Count As Integer

    Public ReadOnly Property ThreadCount() As Integer
      Get
         SyncLock CountLock
           Return _Count
         End SyncLock
      End Get
    End Property

    Public Sub Open()
      Interlocked.Increment(_Count)
    End Sub

    Public Sub Close()
      Interlocked.Decrement(_Count)
      ....
    End Sub

EndClass

My question is, why do we need a lock to implement the readonly ThreadCount property?


回答1:


The lock will force a memory barrier, so that a stale value from the CPU cache isn't read if the last value written was written by a different CPU. The same could be done with Thread.VolatileRead() without locking.




回答2:


This code should be using Interlocked.CompareExchange to access the value in the property getter. Set param3 (comparand) to something that you know cannot be seen in the variable, like Int32.MinValue, and then the function just returns the current value of _count.

If Interlocked operations are used for all accesses to the variable, the lock is redundant since all access via Interlocked class methods is atomic.




回答3:


I have no idea why the author choose to use a lock in one part of the class while utilizing lock-free techniques in other parts. However, I can make an assumption that the author did it to create an explicit memory barrier on the read of the Interger. VB does not contain the equivalent of C#'s volatile keyword so that leaves just 4 other common methods for making the read safe. I have listed these in the order that I would choose for this specific scenario.

  • Interlocked.CompareExchange
  • Thread.VolatileRead
  • Thread.MemoryBarrier
  • SyncLock

The memory barrier is required to prevent the VB or JIT compilers from moving instructions around. The most likely optimization in the absence of a memory barrier is to lift the read outside of a loop. Consider this realistic use of the ThreadCount property.

Sub LoggingThread()
  Do While True
    Trace.WriteLine(ThreadPool.ThreadCount)
  Loop
End Sub

In this example the CLR would likely inline ThreadCount and then potentially "lift" the read of _Count and cache it in a CPU register before the loop begins. The effect would be that the same value would always be displayed.1

1In reality the Trace.WriteLine call itself generates a memory barrier that would cause the code to be safe by accident. The example was intended as a simple illustration of what could happen.




回答4:


It makes no sense, as there is no lock where you modify the property. Maybe the code was not using Interlocked operations before, and was using SyncLock even in Open / Close? In such case SyncLock would be really needed in the read access as well.



来源:https://stackoverflow.com/questions/3853678/why-lock-is-needed-to-implement-a-readonly-int-property

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!