답변:
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'.
lock(this)
것이 표준 조언입니다. 그렇게하면 일반적으로 외부 코드로 인해 개체와 관련된 잠금이 메서드 호출간에 유지되는 것이 불가능하다는 점에 유의해야합니다. 이것은 좋은 것일 수도 있고 아닐 수도 있습니다 . 외부 코드가 임의의 지속 시간 동안 잠금을 유지하는 데 약간의 위험이 있으며 클래스는 일반적으로 그러한 사용을 불필요하게 만들도록 설계해야하지만 항상 실용적인 대안은 아닙니다. 간단한 예로, 컬렉션 이 자체적 으로 ToArray
또는 ToList
메소드를 구현하지 않는 한 ...
there is the common misconception that lock(this) actually modifies the object passed as a parameter, and in some way makes it read-only or inaccessible. This is false
-그 대화는 CLR 객체의 SyncBlock 비트에 관한 것이라고 생각합니다. 공식적으로 이것은 수정 된 객체 자체입니다
사람들이 객체 인스턴스 (예 :) this
포인터를 얻을 수 있으면 동일한 객체를 잠그려고 시도 할 수도 있습니다. 이제 그들은 당신이 this
내부에서 잠겨 있다는 것을 알지 못할 수도 있으므로 문제가 발생할 수 있습니다 (교착 상태)
이 외에도 "너무 많이"잠겨 있기 때문에 나쁜 습관입니다.
예를 들어의 멤버 변수가있을 수 있으며 List<int>
실제로 잠그 어야하는 것은 해당 멤버 변수뿐입니다. 함수에서 전체 객체를 잠그면 해당 함수를 호출하는 다른 것들이 잠금을 기다리는 동안 차단됩니다. 이러한 함수가 멤버 목록에 액세스 할 필요가없는 경우 다른 코드로 인해 응용 프로그램이 아무런 이유없이 대기하고 속도가 느려질 수 있습니다.
MSDN Topic Thread Synchronization (C # 프로그래밍 가이드)을 살펴보십시오.
일반적으로 공개 유형 또는 애플리케이션이 제어 할 수없는 오브젝트 인스턴스에서는 잠금을 피하는 것이 가장 좋습니다. 예를 들어, 공개적으로 인스턴스에 액세스 할 수있는 경우 lock (this)은 문제가 될 수 있습니다. 제어 할 수없는 코드도 객체에서 잠길 수 있기 때문입니다. 이로 인해 둘 이상의 스레드가 동일한 객체의 릴리스를 기다리는 교착 상태 상황이 발생할 수 있습니다.. 개체와 달리 공개 데이터 형식을 잠그면 같은 이유로 문제가 발생할 수 있습니다. 리터럴 문자열은 CLR (공용 언어 런타임)에 의해 삽입되므로 리터럴 문자열에 대한 잠금이 특히 위험합니다. 이것은 전체 프로그램에 대해 주어진 문자열 리터럴의 인스턴스가 하나 있음을 의미하며, 정확히 동일한 객체는 모든 스레드에서 실행중인 모든 응용 프로그램 도메인의 리터럴을 나타냅니다. 결과적으로 응용 프로그램 프로세스의 어느 위치에서나 동일한 내용을 가진 문자열에 대한 잠금은 응용 프로그램에서 해당 문자열의 모든 인스턴스를 잠급니다. 결과적으로 인턴되지 않은 개인 또는 보호 된 구성원을 잠그는 것이 가장 좋습니다. 일부 클래스는 잠금을 위해 특별히 멤버를 제공합니다. 예를 들어 배열 유형은 SyncRoot를 제공합니다. 많은 컬렉션 유형은 SyncRoot 멤버도 제공합니다.
나는이 오래된 스레드 알지만, 사람들은 여전히이 위로보고에 의존 할 수 있기 때문에, 그 지적하는 것이 중요합니다 것 lock(typeof(SomeObject))
보다 훨씬 더 나쁘다 lock(this)
. 라고 한; 그것이 lock(typeof(SomeObject))
나쁜 습관 임을 지적한 Alan에게 진심으로 찬사를 보냅니다 .
의 인스턴스는 System.Type
가장 일반적이고 거친 입자 중 하나입니다. 최소한 System.Type의 인스턴스는 AppDomain에 대해 전역 적이며 .NET은 AppDomain에서 여러 프로그램을 실행할 수 있습니다. 이것은 두 개의 완전히 다른 프로그램이 동일한 유형 인스턴스에서 동기화 잠금을 얻으려고 할 경우 교착 상태가 발생할 때까지 서로 간섭을 일으킬 수 있음을 의미합니다.
따라서 lock(this)
특히 견고한 형태는 아니며 문제를 일으킬 수 있으며 인용 된 모든 이유로 항상 눈썹을 올리십시오. 그러나 개인적으로 패턴 변경을보고 싶어하지만 lock (this) 패턴을 광범위하게 사용하는 log4net과 같이 널리 사용되며 상대적으로 존경 받고 안정적으로 안정적인 코드가 있습니다.
그러나 lock(typeof(SomeObject))
완전히 새롭고 강화 된 웜 캔을 엽니 다.
그만한 가치가 있습니다.
...이 구문에도 똑같은 주장이 적용됩니다.
lock(typeof(SomeObject))
lock(this)
논리적이고 간결하게 보인다 는 것에 동의하지 않습니다 . 엄청나게 거친 잠금이며 다른 코드는 객체를 잠 가서 내부 코드에 간섭을 일으킬 수 있습니다. 더 세밀한 잠금을 취하고 더 세밀한 제어를 가정하십시오. 무엇을 lock(this)
그것은 훨씬 더 이상의 점이다가는가 않습니다 lock(typeof(SomeObject))
.
사무실에 부서의 공유 리소스 인 숙련 된 비서가 있다고 가정합니다. 가끔, 당신은 당신의 동료 중 한 명이 이미 그것을 요구하지 않기를 바라는 임무 만 있기 때문에 그들에게 달려갑니다. 일반적으로 짧은 시간 동안 만 기다려야합니다.
돌보는 것은 공유하기 때문에 관리자는 고객이 비서를 직접 사용할 수도 있다고 결정합니다. 그러나 이것은 부작용이 있습니다. 고객이이 고객을 위해 일하는 동안 고객에게 소유권을 주장 할 수도 있고 작업의 일부를 실행해야 할 수도 있습니다. 소유권 주장이 더 이상 계층 구조가 아니기 때문에 교착 상태가 발생합니다. 고객이 처음부터 고객에게 소유권을 주장하지 못하게함으로써 이러한 문제를 모두 방지 할 수있었습니다.
lock(this)
우리가 본 것처럼 나쁘다. 외부 객체가 객체를 잠글 수 있으며 클래스를 사용하는 사람을 제어하지 않기 때문에 누구나 객체를 잠글 수 있습니다. 이는 위에서 설명한 정확한 예입니다. 다시, 해결책은 물체의 노출을 제한하는 것입니다. 그러나, 당신이있는 경우 private
, protected
또는 internal
클래스는 당신은 이미 개체에 고정되어있는 사용자를 제어 할 수 있습니다 당신은 당신이 스스로 코드를 작성한 것이니까. 따라서 메시지는 다음과 같이 노출하지 마십시오 public
. 또한 유사한 시나리오에서 잠금을 사용하면 교착 상태를 피할 수 있습니다.
이것과 완전히 반대되는 것은 최악의 시나리오 인 앱 도메인 전체에서 공유되는 리소스를 잠그는 것입니다. 그것은 당신의 비서를 외부에 두어 거기에있는 모든 사람이 그들에게 요구할 수있게하는 것과 같습니다. 결과는 완전히 혼돈입니다-또는 소스 코드 측면에서 : 그것은 나쁜 생각이었습니다. 버리고 다시 시작하십시오. 어떻게 그렇게합니까?
대부분의 사람들이 지적한 것처럼 유형은 앱 도메인에서 공유됩니다. 그러나 우리가 사용할 수있는 더 좋은 것들이 있습니다 : 문자열. 그 이유는 문자열 이 풀링되기 때문입니다 . 즉, 앱 도메인에서 동일한 내용을 가진 두 개의 문자열이있는 경우 정확히 동일한 포인터를 가질 가능성이 있습니다. 포인터가 잠금 키로 사용되므로 기본적으로 "정의되지 않은 동작 준비"와 동의어입니다.
마찬가지로 WCF 객체, HttpContext.Current, Thread.Current, Singleton (일반) 등을 잠그면 안됩니다.이 모든 것을 피하는 가장 쉬운 방법은 무엇입니까? private [static] object myLock = new object();
공유 자원을 잠그는 경우이 포인터를 잠그는 것은 좋지 않을 수 있습니다 . 공유 리소스는 정적 변수 또는 컴퓨터의 파일, 즉 클래스의 모든 사용자가 공유하는 파일 일 수 있습니다. 그 이유는이 포인터가 클래스가 인스턴스화 될 때마다 메모리의 위치에 대한 다른 참조를 포함하기 때문입니다. 그래서, 이상 잠금 이 클래스의 인스턴스를 한 번에하는 동안 잠금과 다른 이 클래스의 다른 인스턴스에.
이 코드를 확인하여 무슨 뜻인지 확인하십시오. 콘솔 응용 프로그램에서 기본 프로그램에 다음 코드를 추가하십시오.
static void Main(string[] args)
{
TestThreading();
Console.ReadLine();
}
public static void TestThreading()
{
Random rand = new Random();
Thread[] threads = new Thread[10];
TestLock.balance = 100000;
for (int i = 0; i < 10; i++)
{
TestLock tl = new TestLock();
Thread t = new Thread(new ThreadStart(tl.WithdrawAmount));
threads[i] = t;
}
for (int i = 0; i < 10; i++)
{
threads[i].Start();
}
Console.Read();
}
아래와 같은 새 클래스를 만듭니다.
class TestLock
{
public static int balance { get; set; }
public static readonly Object myLock = new Object();
public void Withdraw(int amount)
{
// Try both locks to see what I mean
// lock (this)
lock (myLock)
{
Random rand = new Random();
if (balance >= amount)
{
Console.WriteLine("Balance before Withdrawal : " + balance);
Console.WriteLine("Withdraw : -" + amount);
balance = balance - amount;
Console.WriteLine("Balance after Withdrawal : " + balance);
}
else
{
Console.WriteLine("Can't process your transaction, current balance is : " + balance + " and you tried to withdraw " + amount);
}
}
}
public void WithdrawAmount()
{
Random rand = new Random();
Withdraw(rand.Next(1, 100) * 100);
}
}
이것에 대한 프로그램 잠금 실행 이 있습니다.
Balance before Withdrawal : 100000
Withdraw : -5600
Balance after Withdrawal : 94400
Balance before Withdrawal : 100000
Balance before Withdrawal : 100000
Withdraw : -5600
Balance after Withdrawal : 88800
Withdraw : -5600
Balance after Withdrawal : 83200
Balance before Withdrawal : 83200
Withdraw : -9100
Balance after Withdrawal : 74100
Balance before Withdrawal : 74100
Withdraw : -9100
Balance before Withdrawal : 74100
Withdraw : -9100
Balance after Withdrawal : 55900
Balance after Withdrawal : 65000
Balance before Withdrawal : 55900
Withdraw : -9100
Balance after Withdrawal : 46800
Balance before Withdrawal : 46800
Withdraw : -2800
Balance after Withdrawal : 44000
Balance before Withdrawal : 44000
Withdraw : -2800
Balance after Withdrawal : 41200
Balance before Withdrawal : 44000
Withdraw : -2800
Balance after Withdrawal : 38400
다음은 myLock 에 대한 프로그램 잠금 실행입니다 .
Balance before Withdrawal : 100000
Withdraw : -6600
Balance after Withdrawal : 93400
Balance before Withdrawal : 93400
Withdraw : -6600
Balance after Withdrawal : 86800
Balance before Withdrawal : 86800
Withdraw : -200
Balance after Withdrawal : 86600
Balance before Withdrawal : 86600
Withdraw : -8500
Balance after Withdrawal : 78100
Balance before Withdrawal : 78100
Withdraw : -8500
Balance after Withdrawal : 69600
Balance before Withdrawal : 69600
Withdraw : -8500
Balance after Withdrawal : 61100
Balance before Withdrawal : 61100
Withdraw : -2200
Balance after Withdrawal : 58900
Balance before Withdrawal : 58900
Withdraw : -2200
Balance after Withdrawal : 56700
Balance before Withdrawal : 56700
Withdraw : -2200
Balance after Withdrawal : 54500
Balance before Withdrawal : 54500
Withdraw : -500
Balance after Withdrawal : 54000
Random rand = new Random();
nvm 을 사용할 때 무엇이 잘못되었는지 파악하기가 어렵 습니다. 반복 된 잔액을 보는 것
Microsoft® .NET 런타임의 성능 설계자 인 Rico Mariani의 http://bytes.com/topic/c-sharp/answers/249277-dont-lock-type-objects 관련 기사가 있습니다.
발췌 :
여기서 기본적인 문제는 type 객체를 소유하지 않으며 누가 다른 객체에 액세스 할 수 있는지 모른다는 것입니다. 일반적으로 생성하지 않은 객체를 잠그고 다른 사람이 액세스 할 수있는 사람을 모르는 것은 매우 나쁜 생각입니다. 교착 상태가 발생합니다. 가장 안전한 방법은 개인 개체 만 잠그는 것입니다.
여기에 대한 좋은 토론도 있습니다 : 이것이 뮤텍스를 올바르게 사용합니까?
lock (this)이 나쁜 이유와 클래스 소비자가 객체를 잠그려고 할 때 교착 상태가 발생할 수 있는 훨씬 간단한 그림이 있습니다 ( 여기 34 번 질문 에서 가져옴). 아래에서는 세 스레드 중 하나만 진행할 수 있으며 다른 두 스레드는 교착 상태입니다.
class SomeClass { public void SomeMethod(int id) { **lock(this)** { while(true) { Console.WriteLine("SomeClass.SomeMethod #" + id); } } } } class Program { static void Main(string[] args) { SomeClass o = new SomeClass(); lock(o) { for (int threadId = 0; threadId < 3; threadId++) { Thread t = new Thread(() => { o.SomeMethod(threadId); }); t.Start(); } Console.WriteLine(); }
이 문제를 해결하기 위해이 사람은 잠금 대신 Thread.TryMonitor (시간 초과 포함)를 사용했습니다.
Monitor.TryEnter(temp, millisecondsTimeout, ref lockWasTaken); if (lockWasTaken) { doAction(); } else { throw new Exception("Could not get lock"); }
https://blogs.appbeat.io/post/c-how-to-lock-without-deadlocks
SomeClass
바꾸면 여전히 동일한 교착 상태가 발생합니다. 또한 기본 클래스의 잠금이 Program의 다른 개인 인스턴스 멤버에서 수행되면 동일한 잠금이 발생합니다. 따라서이 답변이 오도되고 부정확하지 않은지 확실하지 않습니다. 여기에서 그 동작을보십시오 : dotnetfiddle.net/DMrU5h
클래스의 인스턴스를 볼 수있는 코드 덩어리도 해당 참조를 잠글 수 있기 때문입니다. 참조해야하는 코드 만 참조 할 수 있도록 잠금 객체를 숨기고 (캡슐화) 싶습니다. 키워드 this는 현재 클래스 인스턴스를 참조하므로 많은 수의 항목이 참조 할 수 있으며 스레드 동기화를 수행하는 데 사용할 수 있습니다.
분명히 말하면, 다른 코드 덩어리가 클래스 인스턴스를 사용하여 잠글 수 있고 코드가 적시에 잠금을 얻지 못하게하거나 다른 스레드 동기화 문제를 일으킬 수 있기 때문에 이것은 나쁩니다. 가장 좋은 경우 : 클래스에 대한 참조를 사용하여 잠그는 것은 없습니다. 중간 사례 : 클래스에 대한 참조를 사용하여 잠금을 수행하면 성능 문제가 발생합니다. 최악의 경우 : 클래스의 참조를 사용하여 잠금을 수행하면 실제로 나쁜, 미묘하고 디버그하기 어려운 문제가 발생합니다.
미안하지만, 잠그면 교착 상태가 발생할 수 있다는 주장에 동의하지 않습니다. 교착 상태와 굶주림의 두 가지를 혼동하고 있습니다.
여기에서 의 차이를 나타내는 사진이다.
결론 스레드 기아가 문제가되지 않는 경우
에도 안전하게 사용할 수 있습니다 lock(this)
. 굶주린 실을 사용하는 실이 lock(this)
물체를 잠근 상태에서 자물쇠로 끝날 때, 결국에는 영원한 기아로 끝날 것이라는 점을 명심 해야합니다.)
lock(this)
-이런 종류의 코드는 단순히 잘못되었습니다. 교착 상태라고 부르는 것이 약간 모욕적이라고 생각합니다.
잠금 (이것)이 좋지 않은 이유를 설명하는 다음 링크를 참조하십시오.
http://blogs.msdn.com/b/bclteam/archive/2004/01/20/60719.aspx
따라서 해결책은 개인 객체 (예 : lockObject)를 클래스에 추가하고 아래에 표시된대로 코드 명령문을 잠금 문 안에 배치하는 것입니다.
lock (lockObject)
{
...
}
다음은 더 간단한 (IMO) 샘플 코드입니다. ( LinqPad 에서 작동하며 다음 네임 스페이스를 참조하십시오 : System.Net 및 System.Threading.Tasks)
기억해야 할 것은 lock (x)은 기본적으로 구문 설탕이며 그것이하는 일은 Monitor.Enter를 사용한 다음 try, catch, finally 블록을 사용하여 Monitor.Exit를 호출한다는 것입니다. 참조 : https://docs.microsoft.com/en-us/dotnet/api/system.threading.monitor.enter (비고 섹션)
또는 try… finally 블록에 Enter 및 Exit 메서드를 래핑하는 C # 잠금 문 (Visual Basic의 SyncLock 문)을 사용하십시오.
void Main()
{
//demonstrates why locking on THIS is BADD! (you should never lock on something that is publicly accessible)
ClassTest test = new ClassTest();
lock(test) //locking on the instance of ClassTest
{
Console.WriteLine($"CurrentThread {Thread.CurrentThread.ManagedThreadId}");
Parallel.Invoke(new Action[]
{
() => {
//this is there to just use up the current main thread.
Console.WriteLine($"CurrentThread {Thread.CurrentThread.ManagedThreadId}");
},
//none of these will enter the lock section.
() => test.DoWorkUsingThisLock(1),//this will dead lock as lock(x) uses Monitor.Enter
() => test.DoWorkUsingMonitor(2), //this will not dead lock as it uses Montory.TryEnter
});
}
}
public class ClassTest
{
public void DoWorkUsingThisLock(int i)
{
Console.WriteLine($"Start ClassTest.DoWorkUsingThisLock {i} CurrentThread {Thread.CurrentThread.ManagedThreadId}");
lock(this) //this can be bad if someone has locked on this already, as it will cause it to be deadlocked!
{
Console.WriteLine($"Running: ClassTest.DoWorkUsingThisLock {i} CurrentThread {Thread.CurrentThread.ManagedThreadId}");
Thread.Sleep(1000);
}
Console.WriteLine($"End ClassTest.DoWorkUsingThisLock Done {i} CurrentThread {Thread.CurrentThread.ManagedThreadId}");
}
public void DoWorkUsingMonitor(int i)
{
Console.WriteLine($"Start ClassTest.DoWorkUsingMonitor {i} CurrentThread {Thread.CurrentThread.ManagedThreadId}");
if (Monitor.TryEnter(this))
{
Console.WriteLine($"Running: ClassTest.DoWorkUsingMonitor {i} CurrentThread {Thread.CurrentThread.ManagedThreadId}");
Thread.Sleep(1000);
Monitor.Exit(this);
}
else
{
Console.WriteLine($"Skipped lock section! {i} CurrentThread {Thread.CurrentThread.ManagedThreadId}");
}
Console.WriteLine($"End ClassTest.DoWorkUsingMonitor Done {i} CurrentThread {Thread.CurrentThread.ManagedThreadId}");
Console.WriteLine();
}
}
산출
CurrentThread 15
CurrentThread 15
Start ClassTest.DoWorkUsingMonitor 2 CurrentThread 13
Start ClassTest.DoWorkUsingThisLock 1 CurrentThread 12
Skipped lock section! 2 CurrentThread 13
End ClassTest.DoWorkUsingMonitor Done 2 CurrentThread 13
스레드 # 12는 데드락으로 끝나지 않습니다.
DoWorkUsingThisLock
스레드가 문제를 설명하는 데 필요하지 않은 것 같습니다 .
클래스에 'this'또는 클래스의 코드가 인스턴스화하는 객체를 잠그는 코드를 클래스에 가질 수 있다는 규칙을 설정할 수 있습니다. 따라서 패턴을 따르지 않는 경우에만 문제가됩니다.
이 패턴을 따르지 않는 코드로부터 자신을 보호하려면 허용 된 대답이 맞습니다. 그러나 패턴을 따르면 문제가되지 않습니다.
잠금 (이)의 장점은 효율성입니다. 단일 값을 보유한 간단한 "값 개체"가있는 경우 어떻게해야합니까? 랩퍼 일 뿐이며 수백만 번 인스턴스화됩니다. 잠금을 위해 전용 동기화 개체를 만들어야하므로 기본적으로 개체 크기가 두 배가되고 할당 수는 두 배가되었습니다. 성능이 중요 할 때 이점이 있습니다.
할당 횟수 나 메모리 풋 프린트에 신경 쓰지 않으면 다른 답변에 표시된 이유로 잠금을 피하는 것이 좋습니다.
동일한 객체 인스턴스를 사용하는 다른 요청이있을 수 있으므로 인스턴스에 공개적으로 액세스 할 수 있으면 문제가 발생합니다. 개인 / 정적 변수를 사용하는 것이 좋습니다.