2009-05-21 2 views
24

lock(this), lock(typeof(MyType)), lock("a string") 등의 기사와 게시물은 다른 스레드가 동일한 키를 잠그고 교착 상태가 발생할 수 있기 때문에 나쁜 습관입니다. 이 문제를 이해하기 위해 교착 상태를 설명하기 위해 샘플 코드를 만들려고했지만이 문제를 해결할 수는 없었습니다.잠금을 사용하여 교착 상태를 설명하기위한 예제 코드

누군가이 고전적인 문제를 보여주는 간결한 코드를 작성할 수 있습니까? 짧게 유지하십시오. 코드를 더 작은 덩어리로만 소화 할 수 있습니다.

편집 : 내가 생각하기에 lassevk은 잘 요약합니다. 진짜 문제는 당신이 자물쇠를 통제 할 수 없다는 것입니다. 그런 일이 발생하면 잠금이 호출되는 순서를 제어 할 수 없으며 잠재적 인 교착 상태를 허용합니다.

lock(this), lock(typeof(MyType)) 등은 모두 제어 할 수없는 잠금 장치를 선택한 상황입니다.

답변

31

두 개 이상의 잠금 장치가있는 경우에만 교착 상태가 발생합니다. 두 스레드가 다른 스레드가 필요로하는 자원을 보유해야하는 상황이 필요합니다. 즉, 두 개 이상의 자원이 있어야하며 두 스레드가 서로 다른 순서로 스레드를 획득해야합니다.

간단한 예제 : 두 스레드를 가정

// thread 1 
lock(typeof(int)) { 
    Thread.Sleep(1000); 
    lock(typeof(float)) { 
    Console.WriteLine("Thread 1 got both locks"); 
    } 

} 

// thread 2 
lock(typeof(float)) { 
    Thread.Sleep(1000); 
    lock(typeof(int)) { 
    Console.WriteLine("Thread 2 got both locks"); 
    } 
} 

가 서로의 초 이내에 시작, 그들은 두 사람이 내부 잠금 장치에 도달하기 전에 먼저 잠금을 잡기 위해 시간을 가질 것입니다. Sleep() 호출이 없으면 다른 스레드가 시작되기 전에 스레드 중 하나가 두 잠금을 모두 가져오고 해제 할 수있는 가능성이 높습니다.

+2

아하, 내가 게시 한 동안 똑같은 샘플을 쓰고 있었다 :) :)하지만 long과 int를 선택했다. – Maghis

+2

아주 좋은 예 : 교착 상태를 만드는 핵심 요소는 ** 다른 ** 주문에서 두 개의 리소스를 잠그고있다. –

+0

그렇습니다. 이것은 레벨 고정 된 잠금과 같은 기술이 구축되는 원리이며 교착 상태를 피할 수있는 이유는 무엇입니까? – Maghis

3

물론 여기 있습니다.

교착 상태에 대한 일반적인 예는 여러 개의 잠금을 획득하고 둘 이상의 스레드가 서로를 기다리는 경우입니다. 이 같은 고정 예를 들어

, 두 개의 스레드 :

Thread 1    Thread 2 
Lock "A"    Lock "B" 
Lock "B"    Lock "A" <-- both threads will stop dead here 
            waiting for the lock to be come 
            available. 

그러나, 나는 그와 함께 귀찮게하지 않았다이 예에서, 난 그냥 하나 개의 스레드가 무기한으로 잠글 수 있습니다. 잠금 장치에 대한 통제력을 잃고 싶지 않으므로 고의적 인 예제이지만 백그라운드 스레드가 이와 같이 주 스레드를 완전히 차단할 수 있다는 사실은 좋지 않습니다.

using System; 
using System.Threading; 

namespace ConsoleApplication7 
{ 
    public class Program 
    { 
     public static void Main(string[] args) 
     { 
      LockableClass lockable = new LockableClass(); 
      new Thread(new ParameterizedThreadStart(BackgroundMethod)).Start(lockable); 
      Thread.Sleep(500); 
      Console.Out.WriteLine("calling Reset"); 
      lockable.Reset(); 
     } 

     private static void BackgroundMethod(Object lockable) 
     { 
      lock (lockable) 
      { 
       Console.Out.WriteLine("background thread got lock now"); 
       Thread.Sleep(Timeout.Infinite); 
      } 
     } 
    } 

    public class LockableClass 
    { 
     public Int32 Value1 { get; set; } 
     public Int32 Value2 { get; set; } 

     public void Reset() 
     { 
      Console.Out.WriteLine("attempting to lock on object"); 
      lock (this) 
      { 
       Console.Out.WriteLine("main thread got lock now"); 
       Value1 = 0; 
       Value2 = 0; 
      } 
     } 
    } 

} 
+2

댓글 정확하게 "교착 상태"가 다중 잠금 시나리오에서 설명한 것임을 나타내지 만 여기에서 주요 문제는 잠금이 실제 교착 상태인지 여부가 아니라 잠금을 제어 할 수 없다는 것입니다. 당신은 자물쇠에 대한 통제력을 잃고 싶지 않습니다. –

+0

무기한 잠긴 스레드가 교착 상태로 간주됩니까? –

-1

문제는 lock ("문자열")이 싱글 톤에서 잠기는 것입니다. 즉, 동일한 잠금을 사용하는 다른 오브젝트는 무한 대기 상태가 될 수 있습니다. 예를 들어

:이 경우

using System; 
using System.Threading; 

namespace ThreadLock 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      lock ("my lock") 
      { 
       ManualResetEvent evt = new ManualResetEvent(false); 
       WorkerObject worker = new WorkerObject(evt); 
       Thread t = new Thread(new ThreadStart(worker.Work)); 
       t.Start(); 
       evt.WaitOne(); 
      } 
     } 
    } 

    class WorkerObject 
    { 
     private ManualResetEvent _evt; 
     public WorkerObject(ManualResetEvent evt) 
     { 
      _evt = evt; 
     } 
     public void Work() 
     { 
      lock ("my lock") 
      { 
       Console.WriteLine("worked."); 
       _evt.Set(); 
      } 
     } 
    } 
} 

는, 호출 코드는 문자열에 대한 잠금 후 작업자 객체를하게 만듭니다. Work()의 ​​worker 객체는 C#의 싱글 톤인 동일한 문자열을 잠급니다. 발신자가 자물쇠를 소유하고 결코 오지 않을 신호를 기다리고 있기 때문에 교착 상태가됩니다.

+1

같은 스레드가 두 locsk를 실행하려고 시도하기 때문에 교착 상태가 발생하지 않으므로 충돌이 없습니다. ob.Work()가 다른 스레드에서 실행되면 조만간 교착 상태가되지 않습니다. 조만간 첫 번째 잠금이 해제 될 것이기 때문입니다. 교착 상태가 발생하기 전에 두 스레드가 서로 기다리는 것을 막아야합니다. – jalf

+0

수정 된 코드입니다. – plinth

1

이것은 꽤 표준입니다. 순서대로 자물쇠를 잡고 자물쇠로자는. 할 일이 두 가지 잘못되었습니다.:)

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading; 

namespace DeadLock 
{ 
    public class Program 
    { 
     static void Main(string[] args) 
     { 
      var ddt = new DontDoThat(); 

      ddt.Go(); 
     } 
    } 

    public class DontDoThat 
    { 
     private int _badSharedState = 0; 
     private readonly object _lock1 = new object(); 
     private readonly object _lock2 = new object(); 

     public void Go() 
     { 
      new Thread(BadGuy1).Start(); 
      new Thread(BadGuy2).Start(); 

      Console.WriteLine("Leaving Go!"); 
     } 

     public void BadGuy1() 
     { 
      lock (_lock1) 
      { 
       Thread.Sleep(100); // yeild with the lock is bad 
       lock (_lock2) 
       { 
        _badSharedState++; 
        Console.Write("From Bad Guy #1: {0})", _badSharedState); 
       } 
      } 
     } 
     public void BadGuy2() 
     { 
      lock (_lock2) 
      { 
       lock (_lock1) 
       { 
        _badSharedState++; 
        Console.Write("From Bad Guy #2: {0})", _badSharedState); 
       } 
      } 
     } 
    } 
} 
3

액세스 할 수있는 사용자를 제어 할 수없는 것을 잠그지 말아야한다는 아이디어가 있습니다.

유형 객체는 모든 .net 코드 조각에서 볼 수있는 싱글 톤이므로 외부에서 "this"객체의 자물쇠를 제어 할 수 없습니다.

똑같은 것은 문자열을위한 것입니다 : 문자열은 불변이므로 프레임 워크는 "하드 코딩 된"문자열의 인스턴스 하나만 유지하고 풀에 넣습니다 (문자열은 인턴됩니다). 문자열 "hello"를 코딩하면 항상 같은 비난을 받게됩니다.

는 다음과 같은 예를 생각해 당신이 당신의 슈퍼 개별 통화 단지 Thread1을 썼다, Thread2는 당신이 백그라운드 스레드에서 사용하는 일부 라이브러리를 호출하는 동안 ... 다른 답변에

void Thread1() 
{ 
    lock (typeof(int)) 
    { 
    Thread.Sleep(1000); 
    lock (typeof(long)) 
     // do something 
    } 
} 

void Thread2() 
{ 
    lock (typeof(long)) 
    { 
    Thread.Sleep(1000); 
    lock (typeof(int)) 
     // do something 
    } 
} 
0
class Character 
{ 
    public Character Other; 
    public string Name; 
    private object locker = new object(); 

    public Character(string name) 
    { 
     Name = name; 
    } 

    public void Go() 
    { 
     lock (locker) 
     { 
      Thread.Sleep(1000); 
      Console.WriteLine("go in {0}", Name); 
      Other.Go(); 
     } 
    } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     Character a = new Character("A"); 
     Character b = new Character("B"); 
     a.Other = b; 
     b.Other = a; 

     new Thread(a.Go).Start(); 
     b.Go(); 

     Console.ReadLine(); 
    } 
}