lock(this) {...} はなぜ悪いのでしょうか? 質問する

lock(this) {...} はなぜ悪いのでしょうか? 質問する

MSDNドキュメントと言う

public class SomeObject
{
  public void SomeOperation()
  {
    lock(this)
    {
      //Access instance variables
    }
  }
}

「インスタンスがパブリックにアクセスできる場合に問題が発生する」ということです。なぜでしょうか? ロックが必要以上に長く保持されるためでしょうか? それとも、もっと陰湿な理由があるのでしょうか?

ベストアンサー1

ロック ステートメントでこれを使用するのは不適切な形式です。this通常、そのオブジェクトをロックする他のユーザーを制御できないためです。

並列操作を適切に計画するには、起こりうるデッドロック状況を考慮するよう特別な注意を払う必要がありますが、ロック エントリ ポイントの数が不明であると、これが妨げられます。たとえば、オブジェクトへの参照を持つ人は誰でも、オブジェクトの設計者/作成者に知られることなく、そのオブジェクトをロックできます。これにより、マルチスレッド ソリューションの複雑さが増し、正確性に影響する可能性があります。

プライベート フィールドは通常、コンパイラによってアクセス制限が適用され、ロック メカニズムがカプセル化されるため、より適切なオプションです。 を使用すると、thisロック実装の一部が公開され、カプセル化が違反されます。また、ドキュメント化されていない限り、ロックを取得するかどうかは明確ではありませんthis。その場合でも、問題を防ぐためにドキュメントに頼るのは最適ではありません。

lock(this)最後に、パラメータとして渡されたオブジェクトが実際に変更され、何らかの方法で読み取り専用になったりアクセスできなくなったりするというよくある誤解があります。これは誤りです。パラメータとして渡されたオブジェクトは、lock単にキーとして機能します。そのキーにすでにロックが保持されている場合は、ロックを作成できません。それ以外の場合は、ロックが許可されます。

lock文字列は不変であり、アプリケーションの各部分で共有/アクセス可能であるため、ステートメントのキーとして文字列を使用するのは適切ではありません。代わりにプライベート変数を使用する必要がありますObject。インスタンスでも十分です。

例として次の C# コードを実行します。

public class Person
{
    public int Age { get; set;  }
    public string Name { get; set; }

    public void LockThis()
    {
        lock (this)
        {
            System.Threading.Thread.Sleep(10000);
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        var nancy = new Person {Name = "Nancy Drew", Age = 15};
        var a = new Thread(nancy.LockThis);
        a.Start();
        var b = new Thread(Timewarp);
        b.Start(nancy);
        Thread.Sleep(10);
        var anotherNancy = new Person { Name = "Nancy Drew", Age = 50 };
        var c = new Thread(NameChange);
        c.Start(anotherNancy);
        a.Join();
        Console.ReadLine();
    }

    static void Timewarp(object subject)
    {
        var person = subject as Person;
        if (person == null) throw new ArgumentNullException("subject");
        // A lock does not make the object read-only.
        lock (person.Name)
        {
            while (person.Age <= 23)
            {
                // There will be a lock on 'person' due to the LockThis method running in another thread
                if (Monitor.TryEnter(person, 10) == false)
                {
                    Console.WriteLine("'this' person is locked!");
                }
                else Monitor.Exit(person);
                person.Age++;
                if(person.Age == 18)
                {
                    // Changing the 'person.Name' value doesn't change the lock...
                    person.Name = "Nancy Smith";
                }
                Console.WriteLine("{0} is {1} years old.", person.Name, person.Age);
            }
        }
    }

    static void NameChange(object subject)
    {
        var person = subject as Person;
        if (person == null) throw new ArgumentNullException("subject");
        // You should avoid locking on strings, since they are immutable.
        if (Monitor.TryEnter(person.Name, 30) == false)
        {
            Console.WriteLine("Failed to obtain lock on 50 year old Nancy, because Timewarp(object) locked on string \"Nancy Drew\".");
        }
        else Monitor.Exit(person.Name);

        if (Monitor.TryEnter("Nancy Drew", 30) == false)
        {
            Console.WriteLine("Failed to obtain lock using 'Nancy Drew' literal, locked by 'person.Name' since both are the same object thanks to inlining!");
        }
        else Monitor.Exit("Nancy Drew");
        if (Monitor.TryEnter(person.Name, 10000))
        {
            string oldName = person.Name;
            person.Name = "Nancy Callahan";
            Console.WriteLine("Name changed from '{0}' to '{1}'.", oldName, person.Name);
        }
        else Monitor.Exit(person.Name);
    }
}

コンソール出力

'this' person is locked!
Nancy Drew is 16 years old.
'this' person is locked!
Nancy Drew is 17 years old.
Failed to obtain lock on 50 year old Nancy, because Timewarp(object) locked on string "Nancy Drew".
'this' person is locked!
Nancy Smith is 18 years old.
'this' person is locked!
Nancy Smith is 19 years old.
'this' person is locked!
Nancy Smith is 20 years old.
Failed to obtain lock using 'Nancy Drew' literal, locked by 'person.Name' since both are the same object thanks to inlining!
'this' person is locked!
Nancy Smith is 21 years old.
'this' person is locked!
Nancy Smith is 22 years old.
'this' person is locked!
Nancy Smith is 23 years old.
'this' person is locked!
Nancy Smith is 24 years old.
Name changed from 'Nancy Drew' to 'Nancy Callahan'.

おすすめ記事