[解決済み] lock(this){...}はなぜダメなのですか?
質問
その MSDNドキュメント には次のように書かれています。
public class SomeObject
{
public void SomeOperation()
{
lock(this)
{
//Access instance variables
}
}
}
は、インスタンスがパブリックにアクセスできる場合は問題です"。なぜでしょうか?ロックが必要以上に長く保持されるからでしょうか?それとも、何かもっと陰湿な理由があるのでしょうか?
解決方法は?
を使用するのは悪いことです。
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'.
関連
-
[解決済み】「The breakpoint will not currently be hit」を改善するには?このドキュメントにはシンボルが読み込まれていません。" という警告はどうすれば改善されますか?
-
[解決済み] メンバー '<メンバー名>' にインスタンス参照でアクセスできない
-
[解決済み】 C# 条件演算子エラー 代入、call、increment、decrement、await、new object 式のみ文として使用可能です。
-
[解決済み】インデックスが範囲外でした。コレクションパラメータname:indexのサイズより小さく、非負でなければなりません。
-
[解決済み] なぜList<T>を継承しないのですか?
-
[解決済み] EqualsメソッドがオーバーライドされたときにGetHashCodeをオーバーライドすることが重要な理由は何ですか?
-
[解決済み] C#でHashtableよりDictionaryが好まれる理由とは?
-
[解決済み] 揮発性 vs. 連動性 vs. ロック性
-
[解決済み] lockステートメントは、ボンネットの中で何をするのでしょうか?
-
[解決済み】Javaにおけるvolatileとsynchronizedの違いについて
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】プログラム実行中に1秒待つ
-
[解決済み】C#で四捨五入する方法
-
[解決済み】Unity3DでOnTriggerEnterが動作しない件
-
[解決済み】非静的メソッドはターゲットを必要とする
-
[解決済み] 2つのリストを結合する
-
[解決済み】2年前のMSDateを把握する【クローズド
-
[解決済み】aspNetCore 2.2.0 - AspNetCoreModuleV2 エラー
-
[解決済み】パラメータ付きRedirectToAction
-
[解決済み】スレッド終了またはアプリケーションの要求により、I/O操作が中断されました。
-
[解決済み] 新しいエンクスの際に古い値を自動的にデキューする固定サイズのキュー