Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/310.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么锁定要更改的对象是一种不好的做法?_C#_Multithreading_Locking - Fatal编程技术网

C# 为什么锁定要更改的对象是一种不好的做法?

C# 为什么锁定要更改的对象是一种不好的做法?,c#,multithreading,locking,C#,Multithreading,Locking,为什么在下面的代码中使用锁是一种不好的做法,我假设这是一种基于中的答案的不好的做法 private void dosomethingussess() { List otherProductList=新列表(); Parallel.ForEach(myOriginalProductList,product=> { //为了简洁起见,这里删除了一些代码 //这里还有一些代码:) 锁(其他产品列表) { 添加((ipproduct)product.Clone()); } }); } 答案中过度提到这

为什么在下面的代码中使用锁是一种不好的做法,我假设这是一种基于中的答案的不好的做法

private void dosomethingussess()
{
List otherProductList=新列表();
Parallel.ForEach(myOriginalProductList,product=>
{
//为了简洁起见,这里删除了一些代码
//这里还有一些代码:)
锁(其他产品列表)
{
添加((ipproduct)product.Clone());
}
});
}
答案中过度提到这是一种不好的做法,但他们没有说明原因

注意:请忽略代码的有用性,这只是一个示例,我知道它一点用处都没有

来自C语言参考:

通常,避免锁定公共类型或代码无法控制的实例。常见的构造
lock(this)
lock(typeof(MyType))
lock(“myLock”)
违反了以下准则:

lock(this)
如果实例可以公开访问,则会出现问题

lock(typeof(MyType))
是一个问题,如果
MyType
可以公开访问

lock(“myLock”)
是一个问题,因为该过程中有任何其他代码 使用相同的字符串,将共享相同的锁

最佳实践是定义要锁定的私有对象或私有对象 静态对象变量,用于保护所有实例共用的数据

在您的情况下,我会阅读上述指南,认为锁定您将要修改的收藏是一种糟糕的做法。例如,如果您编写了以下代码:

lock (otherProductList) 
{
    otherProductList = new List<IProduct>(); 
}
public class Program
{
    private static void Main(string[] args)
    {
        new MyClass().Test(z => SomeMethod(z));
    }

    private static void SomeMethod(IEnumerable<IProduct> myReference)
    {
        Parallel.ForEach(myReference, item =>
        {
            lock (myReference)
            {
                // Some stuff here
            }
        });
    }
}
lock(其他产品列表)
{
otherProductList=新列表();
}
…那么你的锁就一文不值了。出于这些原因,建议使用专用的
对象
变量进行锁定

请注意,这并不意味着如果使用发布的代码,应用程序将崩溃。“最佳实践”通常被定义为提供易于重复的、技术上更具弹性的模式。也就是说,如果您遵循最佳实践并拥有一个专用的“锁对象”,那么您就不太可能编写基于锁的坏代码;如果你不遵循最佳实践,也许百分之一,你会被一个容易避免的问题所困扰


此外(更一般地说),使用最佳实践编写的代码通常更容易修改,因为您可以减少对意外副作用的担心。

这可能不是一个好主意,因为如果其他人使用相同的对象引用执行
锁定,则可能会出现死锁如果有可能在您自己的代码之外访问您锁定的对象,则其他人可能会破坏您的代码

根据您的代码想象以下示例:

namespace ClassLibrary1
{
    public class Foo : IProduct
    {
    }

    public interface IProduct
    {
    }

    public class MyClass
    {
        public List<IProduct> myOriginalProductList = new List<IProduct> { new Foo(), new Foo() };

        public void Test(Action<IEnumerable<IProduct>> handler)
        {
            List<IProduct> otherProductList = new List<IProduct> { new Foo(), new Foo() };
            Parallel.ForEach(myOriginalProductList, product =>
            {
                lock (otherProductList)
                {
                    if (handler != null)
                    {
                        handler(otherProductList);
                    }

                    otherProductList.Add(product);
                }
            });
        }
    }
}
命名空间类库1
{
公共类Foo:IProduct
{
}
公共接口IPProduct
{
}
公共类MyClass
{
public List myOriginalProductList=新列表{new Foo(),new Foo()};
公共无效测试(操作处理程序)
{
List otherProductList=新列表{new Foo(),new Foo()};
Parallel.ForEach(myOriginalProductList,product=>
{
锁(其他产品列表)
{
if(处理程序!=null)
{
处理程序(其他产品列表);
}
其他产品列表。添加(产品);
}
});
}
}
}
现在,您编译库,将其发送给客户,该客户在其代码中写道:

public class Program
{
    private static void Main(string[] args)
    {
        new MyClass().Test(z => SomeMethod(z));
    }

    private static void SomeMethod(IEnumerable<IProduct> myReference)
    {
        Parallel.ForEach(myReference, item =>
        {
            lock (myReference)
            {
                // Some stuff here
            }
        });
    }
}
公共类程序
{
私有静态void Main(字符串[]args)
{
Test(z=>SomeMethod(z));
}
私有静态方法(IEnumerable myReference)
{
Parallel.ForEach(myReference,item=>
{
锁(myReference)
{
//这里有些东西
}
});
}
}
然后,您的客户可能会遇到一个很难调试的死锁,两个使用过的线程中的每一个都在等待
otherProductList
实例不再被锁定


我同意,这种情况不太可能发生,但它说明,如果您的锁定引用以任何可能的方式在您不拥有的代码段中可见,那么最终代码有可能被破坏

这是一个很好的答案,是否存在其他可能导致问题的情况,请检查我是否更新了问题好的,您引用的是MSDN,并重复锁定可公开访问的对象是一种不好的做法,但仍然没有说明为什么这是一种不好的做法。例如,死锁的风险(答案为@ken2k)是另一个原因。好吧,您发布的代码的一个特殊示例是,尽管您的变量是私有的,但没有什么可以阻止您将该列表传递给另一个类中的方法。如果该类将
锁定在作为参数传递的列表上(可能有其他开发人员在另一个组件中编写糟糕的代码),则可能会影响代码的操作和/或性能。这与我的最后一点有关:最佳实践代码更容易修改,因为内部“锁定对象”永远不会传递到其他地方。@jeroenh:是的,死锁的风险是唯一的原因。导致以后找到它们是一种负担。@jeroenh:死锁的风险在任何线程代码中都是一种风险。这并不一定会因为锁定了正在修改的对象而增加。原始帖子中的代码不比使用专用对象更容易死锁,但遵循最佳实践有助于保持这种状态。使用现有代码,
otherProductList
是此代码的本地代码,因此使用它不会有问题