C#多线程代码访问锁内的局部变量

时间:2012-03-14 14:42:00

标签: c# multithreading locking

我的问题与下面的代码有关。我简化了代码来提炼问题。

我知道锁保持foo Hashtable变量不被改变,但是锁之外的变量呢?我们在代码中看到一些奇怪的行为,看起来像这样,这就是出现的问题。感谢您的任何意见。

using System.Collections;

namespace MultithreadScratch01
{
    public class ThreadFoo
    {
        public void Foo(Stub2 stub2, Hashtable foo)
        {
            Stub1 bar;

            var prop1 = stub2.Prop1;
            var prop2 = stub2.Prop2;
            var prop3 = stub2.Prop3;

            var hash = string.Format("{0}_{1}_{2}", prop1, prop2, prop3);

            lock(foo)
            {
                if(!foo.Contains(hash))
                {
                    bar = new Stub1 {Foo = "some arbitrary string", Bar = 123};
                    foo.Add(hash, bar);
                }
            }

        }
        public class Stub1
        {
            public string Foo { get; set; }
            public int Bar { get; set; }
        }
        public class Stub2
        {
            public string Prop1 { get; set; }
            public string Prop2 { get; set; }
            public string Prop3 { get; set; }

        }
    }
}

4 个答案:

答案 0 :(得分:9)

你在这里做的是多种方式的“最糟糕的做法”。

首先,无法保证引用该哈希表的其他线程在读取或写入之前也将其锁定。这就是为什么这种技术如此糟糕;很难做到这一点。

其次,无法保证引用该哈希表实例的其他线程没有锁定它并且无法将其解锁,因为它们是错误的或敌对的。您可能会让其他人负责您的代码的正确性,这是一个危险的位置。一个好的规则是“永远不会锁定外部代码可以看到的任何东西”。永远不要锁定“this”,永远不要锁定Type对象,等等。有时会对该规则作出例外处理,但我希望有充分的理由。

这里正确的做法是首先使用ConcurrentDictionary。如果你不能这样做,那么我会写一个围绕HashTable的包装器:

sealed class ThreadSafeHashTable
{
    private readonly HashTable hashTable = new HashTable();
    public void Add(object key, object value)
    {
        lock(this.hashTable)
        {
            ...

现在每次调用Add时锁定都是(1),并且(2)只有这个类中的代码可能会锁定,因为被锁定的对象是私有的,永远不会被传递出来。

只有你不能做其中任何一件事,我才会这么做。如果你想以艰难的方式去做,那么你将不得不追踪哈希表可能被使用的每个地方并确保写入正确的锁定代码。

答案 1 :(得分:6)

lock

并不完全正确
  

保持foo Hashtable变量不被更改

简单地;锁定在同一个对象上的两个线程不能同时在锁内。如果您在同一个对象(哈希表)上有任何 lock的代码,它就会直接进入并可能造成损害。重新考虑其他变量......如果有什么东西在改变对象,并且没有锁定你所在的同一个锁对象,那么事情就会变得很奇怪。实际上,如果 锁定在同一个对象上,甚至会有一些边缘情况(这将解决我移动属性 - 读取内部 lock) 。但是,由于这些变量未被“捕获”,因此一旦您拥有值的快照,快照就不会更改。

答案 2 :(得分:3)

你对锁的作用有些误解。它不会阻止其他线程访问或修改foo,但实际上它阻止其他线程进入锁定环境的代码块。如果您在其他地方修改foo,则可能会遇到问题。

由于hash是一个局部变量,所以你不应该看到任何问题。

您可能想尝试使用ConcurrentDictionary而不是Hashtable,它专为多线程访问而设计。

答案 3 :(得分:1)

锁定参数是一个危险的游戏,因为上面的人已经提到,因为你不知道你的方法之外的参数是做什么的。其他人可能会同时修改它。

你可以在你的方法中包装所有代码,以确保一次只有一个线程在该代码中,但是因为你的Stub1和Stub2类不是线程安全的(需要对属性进行锁定)然后甚至做这并不能保证在您阅读它们时不会更改属性。