
오른쪽 직원이 참을성있게 기다리고 있습니다.

왼쪽 직원이 나오면 오른쪽 직원이 들어와서 문을 열어준다.
교착 상태는 직원이 들어와서 영원히 해고되지 않으면 다른 직원이 들어올 수 없는 상황이다.
Lock 키워드를 사용하여 자동 잠금 해제도 시도했습니다. 교착 상태의 가장 기본적이고 단순한 부분에 속합니다.
정상적인 상황에서는 더 높은 수준에서 발생합니다.

입장하려면 둘 다 잠겨 있어야 합니다.

각각 차단

또한 다른 잠금을 획득하려고 시도하십시오.
영원히 서로 둘을 획득하는 경우는 없을 것입니다. 서로 물어뜯고 물어뜯는 상황이 된다.
이러한 상황의 근본적인 이유는 청산 주문이 올바르지 않기 때문입니다.
이를 해결하는 방법은 서로에 대한 규칙을 설정하는 것입니다. 이 자물쇠를 먼저 잠그고 이 자물쇠를 잠근 사람이 두 번째 자물쇠도 잠가야 합니다.

운 좋게도 왼쪽에 있는 아이가 먼저 잠금 1을 얻으면 게임이 종료됩니다.

두 번째 자물쇠를 잠그면 화장실에 들어갈 수 있습니다.
코드를 보면 욕실에 자물쇠를 두 개 사용하는 이유가 무엇입니까?
MMORPG에서 잠금은 종종 클래스 내에 배치됩니다.
class SessionManager
{
static object _lock = new object();
}
class UserManager
{
static object _lock = new object();
}
서로 가까워지다
namespace ServerCore
{
class SessionManager
{
static object _lock = new object();
void TestSession()
{
lock(_lock)
{
}
}
}
class UserManager
{
static object _lock = new object();
void Test()
{
lock(_lock)
{
TestSession();
}
}
}
UserManager가 자체 잠금을 유지하면서 TestSession을 실행하는 경우
이는 UserManager가 먼저 잠금을 획득한 다음 SessionManager가 TestSession을 실행함을 의미합니다.
반대로 SessionManager도 무언가를 할 것입니다.
class SessionManager
{
static object _lock = new object();
void TestSession()
{
lock(_lock)
{
}
}
void Test()
{
lock(_lock)
{
TestUser();
}
}
}
class UserManager
{
static object _lock = new object();
void Test()
{
lock(_lock)
{
TestSession();
}
}
void TestUser()
{
lock(_lock)
{
}
}
}
테스트에서 먼저 자신을 잠근 다음 TestUser라는 다른 참가자를 호출합니다.
그들은 서로 얽히고 서로에게서 자물쇠를 얻으려고 할 것입니다.
이 상황에서 교착 상태가 발생하는지 테스트해 봅시다.
namespace ServerCore
{
class SessionManager
{
static object _lock = new object();
public static void TestSession()
{
lock(_lock)
{
}
}
public static void Test()
{
lock(_lock)
{
UserManager.TestUser();
}
}
}
class UserManager
{
static object _lock = new object();
public static void Test()
{
lock(_lock)
{
SessionManager.TestSession();
}
}
public static void TestUser()
{
lock(_lock)
{
}
}
}
이렇게 static과 public을 설정하고 각 스레드에서 SessionManager에서 Test를 호출하고 UserManager에서 Test를 호출하면 뒤틀린 현상이 발생합니다.
internal class Program
{
static int number = 0;
static object _obj = new object();
static void Thread_1()
{
for (int i = 0; i < 10000; i++)
{
SessionManager.Test();
}
}
static void Thread_2()
{
for (int i = 0; i < 10000; i++)
{
UserManager.Test();
}
}
static void Main(string() args)
{
Task t1 = new Task(Thread_1);
Task t2 = new Task(Thread_2);
t1.Start();
t2.Start();
Task.WaitAll(t1, t2);
Console.WriteLine(number);
}
}
그렇게 했을 때 Console.WriteLine(숫자); 여기서 중단점을 설정하고 실행하면 이 시점에서 교착 상태가 발생하지 않았으며 안전하게 완료되었음을 의미합니다. 당신이 걸을 때

나오지 않는 것을 볼 수 있습니다.
누르면 다 멈춰

여기서 멈추는 것을 볼 수 있습니다.
각 작업자 스레드가 수행하는 작업을 확인하십시오.

TestUser라는 곳에서 물린 것을 볼 수 있습니다.
호출 스택을 보면 SessionManager의 Test가 잠금을 보유하고 있었고 UserManager의 UserManager.TestUser가 해당 잠금을 획득하려고 호출되었습니다.
다른 스레드에서

UserManager의 테스트가 잠금을 유지하고 SessionManager.TestSession(); 전화;
이는 서로 순환하는 사이클에 의해 발생하는 문제임을 알 수 있다.
해결책을 생각해 봅시다.
잠그면 한참을 하다가 포기할 수 있는 방법이 있어야 한다. Monitor.TryEnter()라는 함수가 있는데 오류 자체가 애초에 잠금 구조에 문제가 있다는 뜻이다.
오류를 가정하여 코드를 복제하는 것은 바람직하지 않은 것 같습니다.
잡으려고 하는 것과 비슷합니다. 나는 그것을 사용하는 것보다 그냥 충돌하고 수정하는 것이 더 현명하다고 생각합니다.
이러한 교착 상태가 발생하면 이를 수정하는 것이 훨씬 좋습니다.
어떤 경우에는 복잡한 구조가 될 수 있습니다. 수십 개의 클래스가 있을 때 어떤 프로그래머도 서로가 잠금을 획득하는 방법을 미리 알지 못합니다. 나중에 기본 코드는 초기 사용자가 작성하는 경우가 많기 때문에 잠금이 획득되는 순서를 알기가 더욱 어려워집니다.
대부분의 경우 교착 상태가 발생하면 상황을 감지하고 수정합니다.
예방하기는 어렵지만 발생하는 경우 호출 스택을 추적하여 이유를 찾을 수 있습니다.
구조가 심하게 뒤틀리면 힘들지만 일반적으로 부수고 고치는 것이 좋습니다.
끔찍한 교착 상태는 개발 단계에서는 발생하지 않지만 사용자가 몰리면 폭발하는 경우가 많습니다.
대부분의 경우 UserManager와 SessionManager는 동시에 실행되지 않고 분산되어 실행됩니다.
internal class Program
{
static int number = 0;
static object _obj = new object();
static void Thread_1()
{
for (int i = 0; i < 100; i++)
{
SessionManager.Test();
}
}
static void Thread_2()
{
for (int i = 0; i < 100; i++)
{
UserManager.Test();
}
}
static void Main(string() args)
{
Task t1 = new Task(Thread_1);
Task t2 = new Task(Thread_2);
t1.Start();
Thread.Sleep(100);
t2.Start();
Task.WaitAll(t1, t2);
Console.WriteLine(number);
}
}
Thread.Sleep(100); 으로 텀을 주고 100번만 반복하게 했다.
그런 다음 F5를 다시 누릅니다.

이제 교착 상태가 발생하지 않고 안전하게 통과한 것을 볼 수 있습니다.
동시에 실행하면 교착 상태가 발생하는데, 그런 시점에서 실행하면 아무 일도 일어나지 않고 잘 동작하는 것을 볼 수 있다.
충돌을 수정하는 것이 무책임해 보인다면 더 많은 요령이 있습니다.
일부 프로젝트에서 _lock은 원시 상태로 사용되지 않고 일단 특정 클래스에 매핑됩니다.
namespace ServerCore
{
**class FastLock
{
public int id;
}**
class SessionManager
{
**FastLock l;**
static object _lock = new object();
public static void TestSession()
{
lock(_lock)
{
}
}
public static void Test()
{
lock(_lock)
{
UserManager.TestUser();
}
}
}
class UserManager
{
**FastLock l;**
static object _lock = new object();
public static void Test()
{
lock(_lock)
{
SessionManager.TestSession();
}
}
public static void TestUser()
{
lock(_lock)
{
}
}
}
SessionManager의 FastLock l을 누른 상태에서 다른 자식을 실행하고 현재 ID보다 높으면 문제가 발생했다는 메시지와 함께 충돌하는 경우가 있었습니다. 사실 이 부분은 특정 자식을 호출할 수 있도록 코드를 작성할 때 미리 클래스의 구조를 계획해야 할 수 있는 부분이다. 정말 실용적인지는 모르겠습니다. 그러나 교착 상태가 완전히 사라지는 것을 볼 수는 없습니다.
ID를 제공하여 추적하는 방법. id 호출 시퀀스를 추적하고 차트에 사이클이 있는지 확인하기 위해 차트를 작성하여 교착 상태에 있음을 확인할 수 있습니다. 이 모든 상황에서 교착 상태를 완전히 차단할 수 있는 방법은 없습니다.
사실 Run을 할 때 문제가 되었을 수도 있지만, 지금처럼 타이밍이 안 맞아서 크래시가 나지 않았다고 판단할 수 있었다면, 게임이 시작되기 전에 찾을 수 있었다면 말이 되었을 것입니다. ID를 만들고 추적하는 것은 그런 정신입니다. 생방송을 하기 전에 지나가는 것을 막는다는 식으로 이해하면 됩니다.
이렇게 해도 대부분의 경우 교착 상태를 완전히 방지할 수 없습니다. 생각보다 까다롭기 때문에 실제로 발생하면 고치기가 더 쉽습니다. 졸업 증서
