如果锁对象是锁中的最后一条语句,覆盖它是否不好? [英] Is it bad to overwrite a lock object if it is the last statement in the lock?

查看:45
本文介绍了如果锁对象是锁中的最后一条语句,覆盖它是否不好?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经看过几次了,但是我不确定它是否真的不正确.

I've seen this a couple times now but I'm not sure if it is actually incorrect.

考虑以下示例类:

class Foo
{
    List<string> lockedList = new List<string>();

    public void ReplaceList(IEnumerable<string> items)
    {
        var newList = new List<string>(items);

        lock (lockedList) 
        {
            lockedList = newList;
        }
    }

    public void Add(string newItem)
    {
        lock (lockedList)
        {
            lockedList.Add(newItem);
        }
    }

    public void Contains(string item)
    {
        lock (lockedList)
        {
            lockedList.Contains(item);
        }
    }
}

ReplaceList上有锁时,它会覆盖 lockedList .一旦完成,所有随后的调用者实际上将锁定新值.他们可以在ReplaceList退出其锁定之前输入其锁定.

ReplaceList overwrites lockedList while it has a lock on it. As soon as it does all subsequent callers will actually be locking on the new value. They can enter their lock before ReplaceList exits its lock.

尽管通过替换锁定对象来引发标志,但此代码实际上可能正常工作.只要赋值是锁的最后一条语句,就不再有同步代码可以运行.

Despite raising flags by replacing a lock object, this code might actually work correctly. As long as the assignment is the last statement of the lock there is no more synchronized code to run.

除了确保分配保留在锁块末尾会增加维护成本之外,还有其他原因可以避免这种情况吗?

Besides the increased maintenance cost of ensuring the assignment remains at the end of lock block, is there another reason to avoid this?

推荐答案

因此,首先,由于您访问该字段的方式的具体情况,您提供的特定解决方案并不安全.

So, to start with, no the specific solution that you have provided is not safe, due to the specifics of how you're accessing the field.

>

获得可行的解决方案非常容易.只是不要创建新列表;而是创建一个新列表.相反,请清除它并添加新项目:

Getting a working solution is easy enough. Just don't create a new list; instead, clear it out and add the new items:

class Foo
{
    private List<string> lockedList = new List<string>();

    public void ReplaceList(IEnumerable<string> items)
    {
        lock (lockedList)
        {
            lockedList.Clear();
            lockedList.AddRange(items);
        }
    }

    public void Add(string newItem)
    {
        lock (lockedList)
        {
            lockedList.Add(newItem);
        }
    }

    public void Contains(string item)
    {
        lock (lockedList)
        {
            lockedList.Contains(item);
        }
    }
}

现在您的领域实际上并没有发生变化,并且您不必担心可能引起的所有问题.

Now your field isn't actually changing, and you don't need to worry about all of the problems that that can cause.

关于问题中的代码如何破坏,只需调用 Add Contains 即可读取字段,获取列表,锁定列表,然后让另一个线程替换该字段.当您第二次读取该字段时,在已经锁定该值之后,该值可能已更改,因此您最终将发生变异或从列表中读取其他调用者将不受限制的访问.

As for how the code in the question can break, all it takes is a call to Add or Contains to read the field, get the list, lock the list, and then have another thread replace the field. The when you read the field for a second time, after already getting the value to lock on, the value may have changed, so you'll end up mutating or reading from a list that another caller won't be restricted from accessing.

所有这些,尽管对 lockedList 变量进行变异是一个 really 的坏主意,并且您应该毫无疑问地避免对其进行变异,如上所示,您还可以确保自己实际只读取一次该字段,而不是重复读取它,并且您仍将确保在任何时候都只能从单个线程访问每个列表:

All that said, while mutating the lockedList variable is a really bad idea, and you should unquestionably avoid mutating it, as shown above, you can also ensure that you only actually read the field once, rather than reading from it repeatedly, and you'll still ensure that each list is only ever accessed from a single thread at any one time:

class Foo
{
    private volatile List<string> lockedList = new List<string>();

    public void ReplaceList(IEnumerable<string> items)
    {
        lockedList = new List<string>(items);
    }

    public void Add(string newItem)
    {
        var localList = lockedList;
        lock (localList)
        {
            localList.Add(newItem);
        }
    }

    public void Contains(string item)
    {
        var localList = lockedList;
        lock (localList)
        {
            localList.Contains(item);
        }
    }
}

请注意,这里修复的问题不是改变要锁定的对象从中获取的字段(这不是本质上的问题,尽管这是一个非常糟糕的做法),而是不断从 lock 语句内部从字段的所有用法中获取新值,并期望该值在可能的情况下永远不会改变.

Notice here that the problem that this fixes isn't mutating the field that the object to lock on was fetched from (that's not inherently the problem, although is a very bad practice), but rather constantly getting new values from the field in all usages of it from inside of the lock statements and expecting that value to never change, when it can.

这将很难维护,非常脆弱,并且更难以理解或确保正确性,因此,再次执行此类操作.

This is going to be much harder to maintain, is very fragile, and is much more difficult to understand or ensure the correctness of though, so again, do do things like this.

这篇关于如果锁对象是锁中的最后一条语句,覆盖它是否不好?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆