2010-07-17 5 views
3

나는 필드가 Dictionary<object, IntPtr> 인 클래스가 있습니다. 컬렉션 사전 <object, IntPtr> 완료

IntPtr somePointer = Marshal.AllocHGlobal(/*some desired size*/); 

가 그럼 난 다른 스레드에서 해당 메모리를 사용하려고 해요 : 사용자가 내 클래스의 일부 메소드를 호출 할 때

나는 동적으로 메모리를 할당합니다. 실제로 스레드가 작업을 수행 한 후 Marshal.FreeHGlobal을 통해 할당 된 메모리를 해제하고 컬렉션에서 적절한 키를 제거합니다. 하지만이 스레드가 충돌 할 확률이 있으므로 적절한 마무리를 생각하고 있습니다.

전체 컬렉션을 어떻게 마무리 할 수 ​​있습니까? (일부 스레드가 충돌하여 메모리가 할당 된 채로 남아있는 경우를 대비하여)?

내 성향은 IntPtr에서 SafeHandle으로 변경해야합니다. 이게 도움이 될까요?

+0

가장 좋은 해결책은 관리되지 않는 메모리를 할당하지 않는 것입니다. 일부 p/Invoke 시나리오에만 해당되며 p/Invoke는 문제 설명에 언급되어 있지 않습니다.메모리를 할당하고 해제하기 때문에 실제로이 작업을 수행해야하는 것은 아닙니다. –

+0

새 스레드가 kernel32.dll 함수에서 내 보낸 할당 된 메모리 슬롯을 그냥 통과시킵니다. 이 함수가 끝나면 그 슬롯을 할당 해제 할 것이다. –

답변

1

확실하게 작동 할 수있는 IntPtr입니다 래퍼를 작성. 그러나 항목이 사전에 저장되어있는 한 메모리가 유효한 상태를 유지해야하는 것은 불필요합니다. 이 경우 사용자가 직접 디렉토리에서 항목을 제거하면 자동으로 메모리를 비우는 디렉토리를 만드는 것이 클라이언트 코드에서 훨씬 쉽습니다. 클라이언트가 각 항목에 대해 Dispose()를 호출 할 필요가 없습니다. 이는 항상 장점입니다.

이렇게하려면 IDictionary<object, IntPtr>에서 IDisposable 클래스를 파생시킵니다. 대부분의 메서드 호출을 개인 사전에 위임 할 수 있습니다. 메모리 할당을 위해 커스텀 Add(), 삭제하기 위해 Delete를 원할 것입니다. 그리고 Dispose()와 finalizer를 구현하여 정리하십시오. 코드는하지만 좀 못생긴 :

class MyDictionary : IDictionary<object, IntPtr>, IDisposable { 
    private Dictionary<object, IntPtr> impl = new Dictionary<object, IntPtr>(); 

    public void Add(object key) { 
     IntPtr mem = Marshal.AllocCoTaskMem(666); // Something smarter here... 
     impl.Add(key, mem); 
    } 
    public bool Remove(object key) { 
     if (!impl.ContainsKey(key)) return false; 
     Marshal.FreeCoTaskMem(impl[key]); 
     return impl.Remove(key); 
    } 
    protected void Dispose(bool disposing) { 
     foreach (IntPtr mem in impl.Values) Marshal.FreeCoTaskMem(mem); 
     if (disposing) impl.Clear(); 
    } 
    public void Dispose() { 
     Dispose(true); 
    } 
    ~MyDictionary() { 
     Dispose(false); 
    } 

    // Boilerplate 
    public void Add(object key, IntPtr value) { throw new NotImplementedException(); } 
    public void Add(KeyValuePair<object, IntPtr> item) { throw new NotImplementedException(); } 
    public bool Remove(KeyValuePair<object, IntPtr> item) { throw new NotImplementedException(); } 
    public bool ContainsKey(object key) { return impl.ContainsKey(key); } 
    public ICollection<object> Keys { get { return impl.Keys; }} 
    public bool TryGetValue(object key, out IntPtr value) { return impl.TryGetValue(key, out value); } 
    public ICollection<IntPtr> Values { get {return impl.Values; }} 
    public IntPtr this[object key] { get { return impl[key]; } set { impl[key] = value; } } 
    public void Clear() { impl.Clear(); } 
    public bool Contains(KeyValuePair<object, IntPtr> item) { return impl.Contains(item); } 
    public void CopyTo(KeyValuePair<object, IntPtr>[] array, int arrayIndex) { (impl as ICollection<KeyValuePair<object, IntPtr>>).CopyTo(array, arrayIndex); } 
    public int Count { get { return impl.Count; }} 
    public bool IsReadOnly { get { return (impl as ICollection<KeyValuePair<object, IntPtr>>).IsReadOnly; } } 
    public IEnumerator<KeyValuePair<object, IntPtr>> GetEnumerator() { return impl.GetEnumerator(); } 
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return (impl as System.Collections.IEnumerable).GetEnumerator(); } 
} 

AllocCoTaskMem이 BTW 더 나은 할당을, 그것은 기존의 수하물이 없습니다.

+0

답변 해 주셔서 감사합니다. implizer에 액세스하는 것이 정말 안전합니까? finalizer의 값은 무엇입니까? 나는 그것에 대해 조금 주저하고 있습니다 ... –

+1

파이널 라이저가있는 액세스하는 멤버는 안전하지 않습니다. 사전 <>에는 없습니다. –

+0

그런 경우에는 단순히 클래스의 파이널 라이저에서 모든 사전 키를 찾아 나머지 메모리를 다시 할당 할 수 있습니다. 그러나 Dictionary와 같은 관리 된 항목에 액세스하는 것이 최종 자에게 안전하다는 것을 전혀 알지 못했습니다. 그러므로 묻습니다 –

1

하나의 옵션은 스레드 코드를 try/finally 블록에 넣고 finally 블록을 사용하여 메모리를 해제하는 것입니다. 스레드가 충돌하면 (예외를 throw한다는 뜻입니까?) finally 블록이 메모리가 해제됩니다.

사용자 지정 SafeHandle은 Dispose 메서드에서 메모리를 비우는 데 도움이되지만이 기능을 사용하려면 스레드가 충돌하기 전에 사전에서 항목을 제거해야하므로 더 이상 참조가 필요하지 않습니다. 사전을 통해 SafeHandle. 그렇지 않으면 사전은 자신의 사전의 평생 관리에 따라 사전이 공개되면 메모리가 해제됩니다.

WeakReference을 사용하면 좀 더 이색적인 솔루션을 살펴볼 수도 있습니다.하지만 try/finally와 함께 SafeHandle을 제안 할 수는 있습니다.

+0

좋은 지적이지만 : 내 수업은 주 스레드 (사용자가 노출 된 메서드를 호출하는 스레드)에서 메모리 할당을 수행하고 있습니다. 언급 된 try-catch는 다른 스레드에 있어야합니다. 새 스레드가 절대로 시작되지 않을 확률이 있습니다. 그래서 최종 결정을 생각하고 있습니다. –

+0

@ xenn_33, 스레드가 시작되지 않으면 배경 스레드 스레드가 사전을 탐색하고 "고아"할당을 해제하는 것이 더 나을 수 있습니다. 따라서 스레드에 메모리를 할당하고 스레드가 특정 스레드 시간의 양 또는 이와 유사한 것. 메모리를 할당 한 다음 새 스레드로 전달하고 있습니까? 그렇다면 새 스레드가 메모리를 요청하는 방식으로 스레드가 시작되었음을 알 수 있고 스레드의 finally 블록은 요청한 메모리를 해제 할 수 있습니다. –

+0

그래, 당신은 할당을위한 적절한 장소에 대한 권리입니다. 하지만 OutOfMemory, ThreadAbort 및 기타 예외 때문에 새로운 스폰 된 스레드가 추락 할 경우에만 종료자를 걱정했습니다. –

1

SafeHandle은 FreeHGlobal과 동일한 것으로 생각하지 않는 정리 작업을 수행 할 때 CloseHandle을 호출합니다.

컬렉션이 포함 된 클래스에 전체 Dispose/Finalizer 패턴을 추가하고 사전 및 정리를 수행하는 것이 좋습니다.

또는 HGlobal의 종료자를 사용하여 래퍼를 작성하십시오.

업데이트 :이 유용 할 수 있습니다 - http://social.msdn.microsoft.com/forums/en-US/csharpgeneral/thread/f74b7c3c-12c4-466b-9754-82e9dea8b83e

+0

응답 해 주셔서 감사합니다. >> 컬렉션이 포함 된 클래스에 전체 Dispose/Finalizer 패턴을 추가하고 사전 및 정리를 수행하는 것이 좋습니다. 나의 경향은 finalizer에서 관리 리소스 (사전)에 액세스 할 수 없다는 것입니다 (적어도 극히 나쁜 습관입니다). 내가 잘못? –

+0

윌은 링크가 정말 멋지 네요. 고맙습니다. :) –

+0

당신은 여기 새로 왔지만, 투표를 마치면 끝내야 할 물건입니다. 어쨌든 당신이 좋아했기 때문에 다행입니다. 나는 사전 자체가 finalize되지 않을 것이기 때문에 finalizer에서 사전을 열거하는 데 문제가 있다고 생각하지 않는다. 그러나 어쨌든, 핸들 자체에 대한 안전한 래퍼는 질문을 피하고 깨끗하게 느낀다. –