2011-05-11 6 views
6

서버 응용 프로그램에 다음이 있습니다. 싱글 톤 인 JobManager라는 클래스. JobManager에 일종의 작업을 추가 할 시간인지 계속 확인하는 다른 클래스 인 Scheduler. 클라이언트 응용 프로그램에서 사용자가 서버에 호출을 생성 뭔가를, 동시에교착 상태 델파이 설명/솔루션

TJobManager.Singleton.NewJobItem(parameterlist goes here...); 

: 그렇게 할 시간이되면

는, 스케줄러는 그런 짓을. 내부적으로 서버는 자신에게 메시지를 보내고 그 메시지를 수신하는 클래스 중 하나는 JobManager입니다. JobManager이 메시지를 처리하고 그것의 자신의 방법 호출 목록에 새 작업을 추가하는 시간이다 것을 알고 :

CS.Acquire; 
    try 
    DoSomething; 
    CallAMethodWithAnotherCriticalSessionInternally; 
    finally 
    CS.Release; 
    end; 
다음 NewJobItem 방법에

NewJobItem(parameter list...); 

, 나는 이런 식으로 뭔가를

이 시점에서 시스템이 교착 상태에 도달했습니다 (CS.Acquire). 클라이언트와 서버 응용 프로그램 간의 통신은 Indy 10을 통해 이루어집니다. 메시지 처리기로 보내는 서버 응용 프로그램 메서드를 실행하는 RPC 호출이 Indy 스레드의 컨텍스트에서 실행되고 있다고 생각합니다.

스케줄러에는 자체 스레드가 실행 중이며 JobManager 메소드를 직접 호출합니다. 이 상황이 교착 상태가 발생하기 쉬운가? 누군가 교착 상태가 발생하는 이유를 이해하는 데 도움을 줄 수 있습니까?

클라이언트가 특정 작업을 수행하여 시스템을 잠글 때 가끔은 같은 지점의 중요 섹션에 다른 지점에서 두 번 도달하는이 지점을 찾을 수있었습니다. Scheduler 및 JobManager의 메시지 핸들러 메소드).

내가 그 추가 할 몇 가지 추가 정보를 원하시면 (이 어쨌든 바보가 될 수 있지만,이 ...)을 해봐요 안에이 내부 CS.Release가하고있는 또 다른

CS.Acquire; 
    try 
    Do other stuff... 
    finally 
    CS.Release; 
    end; 

있다 외부 CS에 무엇이든. 알아봐? 그렇다면 스케줄러가 중요 섹션으로 들어가고 모든 잠금 및 잠금 해제가 엉망이 될 수 있습니다.

+0

크리티컬 섹션의 목적은 다른 스레드에서 동시에 실행할 수없는 코드를 보호하기위한 것입니다. 따라서 같은 인스턴스의 중요 섹션에 두 번 이상 도달하면 OK입니다. 비판적 섹션은 그곳에서 일을하고 있으며 그것을 위해 설계되었습니다! 나쁜 점은 획득 스레드가 CS2를 획득하려고 기다리고 있기 때문에 CS2가 해제되지 않는 반면 CS2는 획득 스레드가 CS1 (DeadLock이라고 함)을 획득하기 위해 대기 중이므로 릴리스하지 않는 것입니다. 네가하는 말 때문에 교착 상태를 겪고 있는지 확신 할 수 없다. 왜 확신하니? – jachguate

+0

메시지 핸들러 메소드에 따라 디버깅하는 동안 방금 중요한 섹션을 입력하고 스케줄러에서 오는 CS.Acquire 행의 내 중단 점에 다시 도달했습니다. F8을 다시 누르면 시스템이 중지되었습니다. – ronaldosantana

+5

또한 Windows 메시지 처리 루프에서 교착 상태가 발생할 수 있습니다.ThreadA가 CS1을 잠근 다음 메시지 루프를 반복해야하는 호출을 수행 할 때. 스레드 B는 실제로 CS1 획득을 기다리는 동안 메시지 루프를 중지, 중단 또는 무기한으로 지연시키는 중 ... –

답변

2

JobManager 및 Scheduler가 교착 상태를 일으키는 지 여부를 확실히 알 수있는 시스템 정보가 충분하지 않지만 둘 다 동일한 NewJobItem 메소드를 호출하는 경우에는 문제가되지 않습니다. 모두 동일한 순서로 잠금을 획득합니다.

NewJobItem CS.acquire와 DoSomething CS.acquire가 서로 상호 작용하는지 여부는 질문에 따라 다릅니다. 두 메소드에서 사용 된 잠금 오브젝트가 다른 경우 두 호출은 독립적이지 않아야합니다. 같은 객체이면 잠금 유형에 따라 다릅니다. 만약 당신이 잠그면 재진입 잠금 (re-entrant lock)이된다. (예를 들어, 획득은 동일한 스레드로부터 여러 번 호출 될 수 있고, 획득 및 해제 된 시간을 계산한다) 그러면 문제가되지 않는다. 반면에 재진입을 지원하지 않는 간단한 잠금 개체가있는 경우 DoSomething CS.release가 해당 스레드에 대한 잠금을 해제 한 다음에 획득 한 CS 잠금을 사용하지 않고 CallAMethodWithAnotherCriticalSessionInternally가 실행 중일 수 있습니다. NewJobItem.

두 개 이상의 스레드가 실행 중일 때 교착 상태가 발생하고 각 스레드가 다른 스레드가 현재 작업을 마치기를 기다리고 있기 때문에 자체 스레드를 계속할 수 있습니다.

예를 들어

: 스레드 1은 lock_a을 획득 한 후 중단 및 스레드 2가 지금 실행하고 lock_b을 취득하는 경우 스레드 2의 잠금이 이제 스레드 1에서 반대 순서에 인수되는 것을

Thread 1 executes: 

lock_a.acquire() 
lock_b.acquire() 
lock_b.release() 
lock_a.release() 


Thread 2 executes: 

lock_b.acquire() 
lock_a.acquire() 
lock_a.release() 
lock_b.release() 

공지 사항 계속하기 전에 lock_a가 사용 가능할 때까지 대기하기 시작합니다. 그런 다음 스레드 1은 계속 실행 중이며 다음에 수행 할 작업은 lock_b를 획득하려고 시도하지만 이미 스레드 2에서 수행 중이므로 기다립니다. 마지막으로 스레드 1이 스레드 2가 lock_b를 해제하기를 기다리고 스레드 2가 스레드 1이 lock_a를 해제 할 때까지 대기하는 상황에 있습니다.

이것은 교착 상태입니다.

은 몇 가지 일반적인 해결책이 있습니다

  1. 만 모든 코드에서 하나의 공유 글로벌 잠금을 사용합니다. 이렇게하면 두 개의 잠금을 기다리는 두 개의 스레드를 가질 수 없습니다. 이렇게하면 잠금을 사용할 수 있도록 코드가 많이 대기하게됩니다.
  2. 코드가 한 번에 하나의 잠금을 유지할 수있게하십시오. 메서드 호출의 동작을 알지 못하거나 제어하지 못할 수도 있기 때문에 일반적으로 제어하기가 어렵습니다.
  3. 코드가 동시에 여러 개의 잠금을 모두 획득하고 동시에 모두 해제 할 수있게하고 잠금을 이미 획득 한 상태에서 새 잠금을 가져 오지 못하도록합니다.
  4. 모든 잠금이 동일한 전역 순서로 획득되는지 확인하십시오. 이것은보다 일반적인 기술입니다.

솔루션 4.주의 깊게 프로그래밍해야하며 항상 같은 순서로 잠금/중요 섹션을 확보해야합니다. 디버깅을 돕기 위해 시스템의 모든 잠금 장치 (예 : 각 잠금에 대한 고유 한 정수)에 전역 순서를 배치하고 더 낮은 순위의 잠금을 얻으려고하면 오류가 발생합니다. 현재 스레드가 이미 획득 했음 (예 : new_lock.id < lock_already_acquired.id가 예외를 throw하면)

글로벌 디버깅 지원 도구를 사용하여 순서가 잘못된 잠금을 찾을 수없는 경우, d는 코드에서 모든 잠금을 획득하고 현재 시간, 획득/해제를 호출하는 메소드, 스레드 ID 및 획득중인 잠금 ID와 함께 디버깅 메시지를 인쇄하는 모든 위치를 찾는 것이 좋습니다. 또한 모든 릴리스 호출에서 동일한 작업을 수행하십시오. 그런 다음 교착 상태가 발생할 때까지 시스템을 실행하고 로그 파일에서 어떤 스레드가 어떤 순서로 잠금을 획득했는지 찾으십시오. 그런 다음 어떤 스레드가 잘못된 순서로 잠금에 액세스하고이를 변경하는지 결정하십시오.