2017-03-09 1 views
1

나는이 같은 IDisposable 클래스와 Mutex을 포장하기 위해 노력하고있어 끝나지 :으로 IDisposable와 뮤텍스를 포장하고 테스트하지만 테스트는 결코

public class NamedMutex : IDisposable 
{ 
    private static readonly object _syncLock = new object(); 
    private readonly Mutex _namedMutex; 
    private readonly bool _createdNew; 

    public NamedMutex(string name) 
    { 
     if (string.IsNullOrEmpty(name)) throw new ArgumentNullException("name"); 
     //lock (_syncLock) 
     { 
      _namedMutex = new Mutex(initiallyOwned: false, name: name, createdNew: out _createdNew); 
     } 
     _namedMutex.WaitOne(); 
    } 

    public void Dispose() 
    { 
     //lock (_syncLock) 
     { 
      //if (_createdNew) 
      _namedMutex.ReleaseMutex(); 
      _namedMutex.Dispose(); 
     } 
    } 
} 

당신은 내가했습니다 주석 처리 된 코드에서 볼 수 있듯이 시험을 끝내지 못하기 때문에 위의 구현에 맞지 않거나 뭔가 잘못되었습니다. (아마 죽은 자물쇠는 내가 식별 할 수 없거나 충돌 할 수 있습니다. 비동기 예외).

void Main() 
{ 
    var sw = Stopwatch.StartNew(); 

    var task1 = Task.Run(async() => 
    { 
     using (new NamedMutex("foo")) 
     { 
      Console.WriteLine(3); 
      await Task.Delay(TimeSpan.FromSeconds(3)); 
     } 
    }); 

    var task2 = Task.Run(async() => 
    { 
     using (new NamedMutex("foo")) 
     { 
      Console.WriteLine(2); 
      await Task.Delay(TimeSpan.FromSeconds(2)); 
     } 
    }); 

    Task.WaitAll(task1, task2); 

    //Assert.IsTrue(sw.Elapsed.TotalSeconds >= 5); 
    sw.Elapsed.Dump(); // LINQPad 
} 
+0

당신은'''NamedMutex'''의 생성자에서 중단 점을 넣고 그것이 이제까지'''_namedMutex.WaitOne()을지나 얻는 경우에 우리에게 말해 줄 수,'''라인? –

+0

@MattThomas 예. 'Console.WriteLine ("WaitOne");'을 추가하면'WaitOne'을 한 번만 출력 한 다음 즉시'3'을 출력하고 영원히 멈추게됩니다. – t3chb0t

+0

방금 ​​Visual Studio에서 이것을 테스트했지만 처음에는 올바르게 작동하지만 이후의 모든 시도는 실패합니다. 그런 다음 mutex의 이름을 변경하면 다시 작동하고 이후의 호출에는 다시 실패합니다. 프로그램 종료 후 mutex가 제대로 처리되지 않는 것 같습니다. –

답변

5

이 때문에 await의 발생 :

내가 LINQPad에 맞게 내 테스트입니다. await Task.Delay(..) 후에는 await 성명 이전의 동일한 스레드에 더 이상 있지 않을 수도 있습니다. 따라서 어떤 경우에는 뮤텍스를 소유하지 않은 스레드에서 뮤텍스를 릴리스하려고합니다. 따라서 문제가됩니다. 즉 이전과 await를 한 후, 현재의 thread를 작성하여 확인하기 쉽습니다 :

class Program { 
    public static void Main() { 
     while (true) { 
      var sw = Stopwatch.StartNew(); 

      var task1 = Task.Run(async() => {      
       using (new NamedMutex("foo")) { 
        Console.WriteLine("first before await: " + Thread.CurrentThread.ManagedThreadId); 
        await Task.Delay(TimeSpan.FromSeconds(2)); 
        Console.WriteLine("first after await: " + Thread.CurrentThread.ManagedThreadId); 
       } 
      }); 

      var task2 = Task.Run(async() => {      
       using (new NamedMutex("foo")) { 
        Console.WriteLine("second before await: " + Thread.CurrentThread.ManagedThreadId); 
        await Task.Delay(TimeSpan.FromSeconds(1)); 
        Console.WriteLine("second after await: " + Thread.CurrentThread.ManagedThreadId); 
       } 
      }); 

      Task.WaitAll(task1, task2); 

      //Assert.IsTrue(sw.Elapsed.TotalSeconds >= 5); 
      Console.WriteLine(sw.Elapsed); 
     }    
    } 
} 
+0

방금이 사실을 깨닫고 대답하러 왔습니다. –

+0

아, 그럼 아마'mutex'를'async/await'와 함께 사용하는 것을 잊어 버릴 수 있습니다. – t3chb0t

+0

또한 이것은 현재 'SynchronizationContext'에 의존한다는 점에 유의하십시오. WinForms 또는 WPF와 같은 UI 응용 프로그램에서는 컨텍스트가 UI 스레드의 지속을 마샬링하기 때문에이 문제가 발생하지 않지만 콘솔 응용 프로그램에는 마샬링 할 "주"스레드이므로, 연속 스레드는 임의의 ThreadPool 스레드에서 실행됩니다. –

1

Evk's answer을 확장하고, 해결에 도착, IDisposableMutex을 포장하는 것은 여전히 ​​가능하다. Mutex을 획득하고 릴리스하는 Thread에 대한 완전한 제어권을 갖고 있는지 확인해야하며, 뮤텍스를 획득하고 릴리스하는 사이에 해당 스레드에서 컨텍스트가 전환되지 않도록해야합니다.

그럼 자신의 스레드를 위로 돌리십시오. 뭔가 같은 :

class NamedMutex : IDisposable 
{ 
    private readonly Thread _thread; 
    private readonly ManualResetEventSlim _disposalGate; 
    private readonly Mutex _namedMutex; 
    public NamedMutex(string name) 
    { 
     var constructorGate = new ManualResetEventSlim(); 
     _disposalGate = new ManualResetEventSlim(); 
     _thread = new Thread(() => 
     { 
      // Code here to acquire the mutex 
      _namedMutex = new Mutex(initiallyOwned: false, name: name, createdNew: out _createdNew); 

      constructorGate.Set(); // Tell the constructor it can go on 
      _disposalGate.Wait(); // Wait for .Dispose to be called 

      // Code here to release the mutex 
      _namedMutex.ReleaseMutex(); 
      _namedMutex.Dispose(); 
     }); 
     _thread.Start(); 
     constructorGate.Wait(); 
    } 

    public void Dispose() 
    { 
     _disposalGate.Set(); 
    } 
} 
+0

내 [의견] (http://stackoverflow.com/questions/42698844/wrapping-a-mutex-with-idisposable-and-testing-it-but-the-test-never-ends#comment72521003_42699138)을 참조하십시오. 현재 두 개의 프로세스가 동시에 수정할 수없는 리소스를 식별하기 때문에 이름이 필요합니다. – t3chb0t

+0

@ t3chb0t. 이것은 단지 요지입니다. 그래서''NamedMutex'' 생성자를 통해 이름을 던지고''/'mutex를 얻기위한 코드는''주석이있는 곳에서 이름이 지정된 뮤텍스를 얻습니다. 그 것들을 추가하기 위해 편집했습니다. –

+0

이것은 매번 새로운 스레드를 만드는 것을 포함하지만, 효과가있는 것처럼 보입니다. –