programing

뮤텍스가 폐기 될 때 방출되지 않는 이유는 무엇입니까?

coolbiz 2021. 1. 18. 08:15
반응형

뮤텍스가 폐기 될 때 방출되지 않는 이유는 무엇입니까?


다음 코드가 있습니다.

using (Mutex mut = new Mutex(false, MUTEX_NAME))
{
    if (mut.WaitOne(new TimeSpan(0, 0, 30)))
    {
       // Some code that deals with a specific TCP port
       // Don't want this to run at the same time in another process
    }
}

if블록 내에 중단 점을 설정하고 다른 Visual Studio 인스턴스 내에서 동일한 코드를 실행했습니다. 예상대로 .WaitOne호출이 차단됩니다. 그러나 놀랍게도 첫 번째 인스턴스를 계속 하고 using블록이 종료 되 자마자 두 번째 프로세스에서 버려진 뮤텍스에 대한 예외가 발생합니다.

수정은 다음을 호출하는 것입니다 ReleaseMutex.

using (Mutex mut = new Mutex(false, MUTEX_NAME))
{
    if (mut.WaitOne(new TimeSpan(0, 0, 30)))
    {
       // Some code that deals with a specific TCP port
       // Don't want this to run twice in multiple processes
    }

    mut.ReleaseMutex();
}

이제 예상대로 작동합니다.

내 질문 : 보통의 점은 IDisposable그것이 정리 . 나는 아마도 여러 가지고 볼 수 있습니다 당신은 물건을 넣어 어떤 상태로 대기 하고 자료를 내에서 using블록,하지만 뮤텍스에 대한 핸들이 배치 될 때, 그것은 자동으로 해제되지해야합니까? 즉, 블록 ReleaseMutex에있는 경우 왜 전화를해야 using합니까?

또한 if블록 내의 코드 가 충돌하면 주변에있는 뮤텍스를 버리게 될 것이라고 걱정합니다 .

퍼팅 어떤 혜택이 있습니까 MutexA의 using블록은? 아니면, 그냥해야 새 최대Mutex 예, 시도 / 캐치에 포장 및 통화 ReleaseMutex()이내에는 마지막으로 (기본적으로 내가 정확히 구현 차단 생각 Dispose() 할 것)


문서 는 ( "Remarks"섹션에서) Mutex 객체 인스턴스화 하는 것과 (실제로 동기화가 진행되는 한 특별한 작업을 수행 하지 않는 ) Mutex를 획득 하는 것 (을 사용하여 WaitOne) 사이에 개념적 차이가 있음을 설명합니다. 참고 :

  • WaitOne부울을 반환합니다. 즉, Mutex 획득이 실패 (시간 초과) 될 수 있으며 두 경우 모두 처리되어야합니다.
  • WaitOne반환 되면 true호출하는 스레드가 Mutex를 획득하고을 호출 해야합니다.ReleaseMutex 그렇지 않으면 Mutex가 중단됩니다.
  • 이 반환 될 때 false, 다음 호출 스레드가 전화ReleaseMutex

따라서 인스턴스화보다 뮤텍스에 더 많은 것이 있습니다. using어쨌든 사용해야하는지 여부 에 관해서 Dispose는 (에서 상속 된대로WaitHandle ) 무엇을하는지 살펴 보겠습니다 .

protected virtual void Dispose(bool explicitDisposing)
{
    if (this.safeWaitHandle != null)
    {
        this.safeWaitHandle.Close();
    }
}

보시다시피 Mutex는 출시 되지 않았지만 일부 정리가 관련되어 있으므로 고수하는 using것이 좋은 접근 방식입니다.

진행 방법에 관해서는 물론 try/finally블록을 사용 하여 Mutex를 획득 한 경우 적절하게 해제되도록 할 수 있습니다. 이것은 아마도 가장 간단한 접근 방식 일 것입니다.

Mutex를 획득하지 못한 경우 (a to 를 전달하기 때문에 표시하지 않은 경우)에 대해 정말로 신경 쓰지 않는다면를 구현하는 자체 클래스로 래핑 하고 생성자에서 Mutex를 획득 할 수 있습니다 (사용 인수없이) 내부에서 해제합니다 . 비록 @HansPassant가 언급했듯이 인수를 시도 할 때 두 경우 모두 명시 적으로 처리 해야하는 좋은 이유 가 있는지 여부와 관계없이 무언가 잘못되면 스레드가 무기한 대기하게되므로 이것을 권장하지 않을 것입니다 .TimeSpanWaitOneMutexIDisposableWaitOne()Dispose


이 디자인 결정은 오래 전에 내려졌습니다. 21 년 전, .NET이 구상되거나 IDisposable의 의미론이 고려되기 훨씬 전에. .NET Mutex 클래스는 뮤텍스 에 대한 기본 운영 체제 지원을위한 래퍼 클래스입니다. 생성자는 CreateMutex 를 고정하고 WaitOne () 메서드는 WaitForSingleObject ()를 고정 합니다.

WaitForSingleObject ()의 WAIT_ABANDONED 반환 값은 예외를 생성하는 값입니다.

Windows 디자이너는 뮤텍스를 소유 한 스레드가 종료되기 전에 ReleaseMutex ()를 호출 해야 한다는 엄격한 규칙을 적용했습니다 . 그리고 이것이 스레드가 일반적으로 예외를 통해 예상치 못한 방식으로 종료되었다는 매우 강력한 표시가 아니라면. 이는 매우 심각한 스레딩 버그 인 동기화가 손실되었음을 의미합니다. 같은 이유로 .NET에서 스레드를 종료하는 매우 위험한 방법 인 Thread.Abort ()와 비교해보십시오.

.NET 디자이너는이 동작을 변경하지 않았습니다. 최소한 대기를 수행하는 것 외에 뮤텍스의 상태를 테스트 할 방법이 없기 때문입니다. 당신은 해야한다 ReleaseMutex ()를 호출한다. 그리고 두 번째 스 니펫도 올바르지 않습니다. 획득하지 않은 뮤텍스에서는 호출 할 수 없습니다. if () 문 본문 내부로 이동해야합니다.


뮤텍스의 주요 용도 중 하나는 불변성을 만족하지 않는 상태에서 공유 객체를 볼 수있는 유일한 코드가 객체를 해당 상태에 (잠시 적으로) 넣는 코드임을 확인하는 것입니다. 객체를 수정해야하는 코드의 일반적인 패턴은 다음과 같습니다.

  1. 뮤텍스 획득
  2. 개체의 상태를 무효화하는 변경
  3. 개체를 변경하여 상태를 다시 유효하게 만듭니다.
  4. 뮤텍스 해제

# 2가 시작된 후 # 3이 끝나기 전에 문제가 발생하면 객체는 불변성을 충족하지 않는 상태로 남을 수 있습니다. 적절한 패턴은 뮤텍스를 폐기하기 전에 해제하는 것이므로 코드가 뮤텍스를 해제하지 않고 폐기한다는 사실은 어딘가에 문제가 있음을 의미합니다. 따라서 코드가 뮤텍스에 들어가는 것은 안전하지 않을 수 있지만 (해제되지 않았기 때문에) 뮤텍스가 해제 될 때까지 기다릴 이유가 없습니다 (삭제 되었기 때문에 절대 없을 것입니다). . 따라서 적절한 조치는 예외를 발생시키는 것입니다.

.NET 뮤텍스 객체에 의해 구현 된 것보다 다소 더 좋은 패턴은 "acquire"메소드 IDisposable가 뮤텍스가 아닌 특정 획득을 캡슐화 하는 객체를 반환하도록하는 것입니다. 해당 개체를 삭제하면 뮤텍스가 해제됩니다. 코드는 다음과 같이 보일 수 있습니다.

using(acq = myMutex.Acquire())
{
   ... stuff that examines but doesn't modify the guarded resource
   acq.EnterDanger();
   ... actions which might invalidate the guarded resource
   ... actions which make it valid again
   acq.LeaveDanger();
   ... possibly more stuff that examines but doesn't modify the resource
}

If the inner code fails between EnterDanger and LeaveDanger, then the acquisition object should invalidate the mutex by calling Dispose on it, since the guarded resource may be in a corrupted state. If the inner code fails elsewhere, the mutex should be released since the guarded resource is in a valid state, and the code within the using block won't need to access it anymore. I don't have any particular recommendations of libraries implementing that pattern, but it isn't particularly difficult to implement as a wrapper around other kinds of mutex.


Ok, posting an answer to my own question. From what I can tell, this is the ideal way to implement a Mutex that:

  1. Always gets Disposed
  2. Gets Released iff WaitOne was successful.
  3. Will not get abandoned if any code throws an exception.

Hopefully this helps someone out!

using (Mutex mut = new Mutex(false, MUTEX_NAME))
{
    if (mut.WaitOne(new TimeSpan(0, 0, 30)))
    {
        try
        {
           // Some code that deals with a specific TCP port
           // Don't want this to run twice in multiple processes        
        }
        catch(Exception)
        {
           // Handle exceptions and clean up state
        }
        finally
        {
            mut.ReleaseMutex();
        }
    }
}

Update: Some may argue that if the code within the try block puts your resource in an unstable state, you should not release the Mutex and instead let it get abandoned. In other words, just call mut.ReleaseMutex(); when the code finishes successfully, and not put it within the finally block. The code acquiring the Mutex could then catch this exception and do the right thing.

In my situation, I'm not really changing any state. I'm temporarily using a TCP port and can't have another instance of the program run at the same time. For this reason, I think my solution above is fine but yours may be different.


We need to understand more then .net to know what is going on the start of the MSDN page gives the first hint that someone “odd” is going on:

A synchronization primitive that can also be used for interprocess synchronization.

A Mutex is a Win32Named Object”, each process locks it by name, the .net object is just a wrapper round the Win32 calls. The Muxtex itself lives within the Windows Kernal address space, not your application address space.

In most cases you are better off using a Monitor, if you are only trying to synchronizes access to objects within a single process.


If you need to garantee that the mutex is released switch to a try catch finally block and put the mutex release in the finally block. It is assumed that you own and have a handle for the mutex. That logic needs to be included before release is invoked.


Reading the documentation for ReleaseMutex, it seems the design decision was that a Mutex should be released consciously. if ReleaseMutex isn't called, it signifies an abnormal exit of the protected section. putting the release in a finally or dispose, circumvents this mechanism. you are still free to ignore the AbandonedMutexException, of course.


Be aware: The Mutex.Dispose() executed by the Garbage collector fails because the garbage collection process does not own the handle according Windows.


Dispose depends on WaitHandle to be released. So, even though using calls Dispose, it won't go into affect until the the conditions of stable state are met. When you call ReleaseMutex you're telling the system that you're releasing the resource, and thus, it is free to dispose of it.

ReferenceURL : https://stackoverflow.com/questions/25432596/why-doesnt-mutex-get-released-when-disposed

반응형