2011-03-18 2 views
6

새로운 동시 콜렉션을 읽었으며, 특히 ConcurrentBag가 내 관심을 끌었습니다. ConcurrentBag는 항목을 추적하기 위해 각 개별 스레드에서 로컬 세트를 내부적으로 보유하므로 스레드 자체가 범위를 벗어나면 여전히 ConcurrentBag에 의해 메모리에서 참조됩니다. 즉, 스레드가 요청한 메모리와 원시 리소스를 모두 의미합니까? (.NET 스레드 객체의 정확한 내부 동작을 알지 못해서 실례합니다.)ConcurrentBag에 메모리 리크가 있습니까?

많은 클라이언트가 작업을 추가하는 멀티 스레드 웹 서비스에 대해 1 개의 전역 ConcurrentBack이 있다고 가정 할 수 있습니다. 이러한 작업은 스레드 풀의 스레드에 의해 추가됩니다. 이제 스레드 풀은 스레드를 관리하는 매우 효율적인 방법이지만 작업량에 따라 스레드를 제거하고 만듭니다. 따라서, 그러한 웹 서비스는 근본적인 가방이 여전히 많은 파괴 된 스레드를 참조하고 있기 때문에 때때로 문제가 될 수 있습니다.

나는이 문제를 테스트하기 위해 빠른 응용 프로그램을 만들어 :

static ConcurrentBag<int> bag = new ConcurrentBag<int>(); 
    static void FillBag() { for (int i = 0; i < 100; i++) { bag.Add(i); } } 
    static void PrintState() { Console.WriteLine("Bag size is: {0}", bag.Count); } 
    static void Main(string[] args) 
    { 
     var remote = new Thread(x => 
     { 
      FillBag(); 
      PrintState(); 
     }); 
     // empty bag 
     PrintState(); 
     // first 100 items are added on main thread 
     FillBag(); 
     PrintState(); 
     // second 100 items are added on remote thread 
     remote.Start(); 
     remote.Join(); 
     // since the remote thread is gone out of scope, what happened to its local storage which is part of the bag? 
     PrintState(); 
     // now force a cleanup 
     WeakReference weakRemoteReference = new WeakReference(remote); 
     remote = null; 
     GC.Collect(); 
     GC.WaitForPendingFinalizers(); 
     // Now check if the thread still exists 
     if (weakRemoteReference.IsAlive) 
      Console.WriteLine("Remote thread still exists"); 
     PrintState(); 
     Console.ReadLine(); 

을 그리고 출력은 나의 이야기 확인 :

Bag size is: 0 
Bag size is: 100 
Bag size is: 200 
Bag size is: 200 
Remote thread still exists 
Bag size is: 200 

예상되는이 문제인가를 내 시험에서 실수를 않았다 또는 이것이 설계 결함으로 간주 될 수 있습니까?

+0

원격 스레드가 범위를 벗어났다는 것을 명심해야합니다. "원격 스레드가 완료되었으므로" – Polity

답변

8

ConcurrentBag은 실제로 스레드 로컬 저장소에 항목을 보관하고 스레드를 포기하면 으로 메모리 누수가 발생할 수 있습니다. 그러나 구현은 하나의 스레드 목록에서 항목을 "훔쳐"다른 스레드에 제공 할 수 있습니다. 해당 프로그램을 실행하면

ConcurrentBag<int> MyBag = new ConcurrentBag<int>(); 

void DoIt() 
{ 
    for (int i = 0; i < 10; ++i) 
    { 
     MyBag.Add(i); 
    } 

    ThreadPool.QueueUserWorkItem(EmptyBag); 

    Console.Write("Press Enter:"); 
    Console.ReadLine(); 

    Console.WriteLine("{0} items in bag", MyBag.Count); 
} 

void EmptyBag(object state) 
{ 
    int take; 
    while (MyBag.TryTake(out take)) 
    { 
     Console.WriteLine(take); 
    } 
    Console.WriteLine("Bag is empty"); 
} 

를하고 Enter를 누르 전에 "가방이 비어있는"메시지까지 기다려야 당신이 가방 참 비운 것을 볼 수 있습니다 : 다음 작성하는 경우 당신은 행동에서 볼 수 있습니다.

가방에서 읽는 한 개의 스레드가있는 한 은 결국이 비게됩니다. 모든 항목이 다른 스레드에 의해 추가 된 경우에도 마찬가지입니다.

그래, 가능한 메모리 누수가 있습니다. 실제로는 여러 스레드가 가방에 액세스하는 경우 문제가되지 않을 수도 있습니다.

+0

두 스레드가 여기에 있으며, QueueUserWorkItem이 새 스레드를 생성한다고 추측합니다. . 유출 스레드 (소스 또는 도난당한 복사본)는 어느 스레드입니까? 어떻게하면이 프로세스를 중단하고 정상적으로 종료 할 수 있습니까? ... 작업에서 취소 토큰을 사용하고 있습니다 ... – LamonteCristo

+0

@ makerofthings7 :이 예제 코드에서는 주 스레드가 bag을 소유하고 QueueUserWorkItem에 의해 생성 된 스레드가 bag을 비 웁니다. 이 예제에서는 아무것도 누출되지 않습니다. 일반적으로 가방에 물건을 추가하고 꺼내지 않으면 누출이 발생합니다. 자세한 정보가 필요하면 질문 게시를 고려해보십시오. –

관련 문제