为什么';tc#是否允许锁定空值?

为什么';tc#是否允许锁定空值?,c#,multithreading,locking,C#,Multithreading,Locking,C#不允许锁定空值。我想我可以在锁定它之前检查该值是否为null,但是因为我没有锁定它,另一个线程可能会出现并使该值为null!如何避免这种竞争条件?锁定一个从不为空的值,例如 Object _lockOnMe = new Object(); Object _iMightBeNull; public void DoSomeKungFu() { if (_iMightBeNull == null) { lock (_lockOnMe) { if (_

C#不允许锁定空值。我想我可以在锁定它之前检查该值是否为null,但是因为我没有锁定它,另一个线程可能会出现并使该值为null!如何避免这种竞争条件?

锁定一个从不为空的值,例如

Object _lockOnMe = new Object();
Object _iMightBeNull;
public void DoSomeKungFu() {
    if (_iMightBeNull == null) {
        lock (_lockOnMe) {
            if (_iMightBeNull == null) {
                _iMightBeNull = ...  whatever ...;
            }
        }
    }
}

还要小心避免双重检查锁定的有趣竞争条件:

这里有两个问题:

首先,不要锁定
null
对象。如何区分两个对象(都是
null
)是没有意义的

其次,要在多线程环境中安全地初始化变量,请使用双重检查锁定模式:

if (o == null) {
    lock (lockObj) {
        if (o == null) {
            o = new Object();
        }
    }
}

这将确保另一个线程尚未初始化该对象,并可用于实现单例模式。

您不能锁定空值,因为CLR没有可附加SyncBlock的位置,这允许CLR通过Monitor.Enter/Exit同步对任意对象的访问(这是
lock
内部使用的)

为什么C不允许锁定空值

是迄今为止唯一技术上正确的一个,我接受它。这是因为.NET中的监视器使用附加到所有引用类型的同步块。如果您有一个
null
变量,则它不引用任何对象,这意味着监视器无法访问可用的同步块

我怎样才能避免这种比赛状态


传统的方法是锁定一个对象引用,您知道它永远不会为
null
。如果您发现自己处于无法保证的情况下,那么我会将其归类为非传统方法。除非您更详细地描述可以使用的特定场景,否则我在这里不能提及更多内容导致可为空的锁定目标。

问题的第一部分已经回答,但我想为问题的第二部分添加一些内容

使用不同的对象执行锁定更简单,特别是在这种情况下。 这也解决了在关键部分维护多个共享对象状态的问题,例如员工列表和员工照片列表

此外,当您必须获得对基本类型(如int或decimal等)的锁定时,此技术也很有用

在我看来,如果你像其他人建议的那样使用这种技术,那么你就不需要执行两次空检查。例如,在接受的答案中,Cris使用了两次if条件,因为锁定的对象与实际修改的对象不同,所以这两次条件实际上没有任何区别,如果你锁定的对象不同,那么执行进行第一次空检查是无用的,也是对cpu的浪费

我建议使用以下代码:

object readonly syncRootEmployee = new object();

List<Employee> employeeList = null;
List<EmployeePhoto> employeePhotoList = null;

public void AddEmployee(Employee employee, List<EmployeePhoto> photos)
{
    lock (syncRootEmployee)
    {
        if (employeeList == null)
        {
            employeeList = new List<Employee>();
        }

        if (employeePhotoList == null)
        {
            employeePhotoList = new List<EmployeePhoto>();
        }

        employeeList.Add(employee);
        foreach(EmployeePhoto ep in photos)
        {
            employeePhotoList.Add(ep);
        }
    }
}

Happy threading:)

为什么不使用静态初始化且始终不是null的成员呢?据我所知,null本质上什么都不是。你怎么能把什么都锁上?换句话说,string myString=null声明了string类型的变量,但这就是它的全部——它不作为一个对象存在,因为它没有任何价值。最好将readonly添加到lock对象中,以调用所需的不可变性。为什么锁定可以阻止其他人访问?iMightBeNull?-1:您的代码仍然容易受到链接到的竞争条件的影响<代码>\u iMightBeNull需要声明为易失性。或者,最好只使用
Lazy
进行延迟初始化。@Douglas怎么会这样?只要在访问
\u iMightBeNull
之前始终锁定
\u lockome
,您就不需要volatile。@SourceOverflow:上面的代码在锁定
\u iMightBeNull
之前首先检查它是否为空。您需要了解内存模型,才能理解为什么会出现这种情况。要快速确认,请参阅您与Paul答案的链接已断开
object readonly syncRootIteration = new object();

long iterationCount = 0;
long iterationTimeMs = 0;

public void IncrementIterationCount(long timeTook)
{
    lock (syncRootIteration)
    {
        iterationCount++;
        iterationTimeMs = timeTook;
    }
}

public long GetIterationAvgTimeMs()
{
    long result = 0;

    //if read without lock the result might not be accurate
    lock (syncRootIteration)
    {
        if (this.iterationCount > 0)
        {
            result = this.iterationTimeMs / this.iterationCount;
        }
    }

    return result;
}