Wątek uniemożliwiający zbieranie śmieci właściciela

W bibliotece, którą stworzyłem, mam klasę DataPort, która implementuje funkcjonalność podobną do klasy .NET SerialPort. Rozmawia z jakimś sprzętem i generuje zdarzenie, gdy dane przychodzą przez ten sprzęt. Aby zaimplementować to zachowanie, DataPort tworzy wątek, który ma mieć taki sam czas życia jak obiekt DataPort.Problem jest tak, że gdy DataPort wychodzi poza zakres, tonigdy nie zbiera śmieci

Teraz, ponieważ DataPort rozmawia ze sprzętem (używając pInvoke) i jest właścicielem niektórych niezarządzanych zasobów, implementuje IDisposable. Gdy zadzwonisz do Dispose na obiekcie, wszystko dzieje się prawidłowo. DataPort pozbywa się wszystkich swoich niezarządzanych zasobów i zabija wątek roboczy i odchodzi. Jeśli po prostu pozwolisz DataPortowi wyjść poza zakres, garbage collector nigdy nie wywoła finalizatora, a DataPort pozostanie w pamięci na zawsze. Wiem, że dzieje się tak z dwóch powodów:

Punkt przerwania w finalizatorze nigdy nie zostanie trafionySOS.dll mówi mi, że DataPort wciąż żyje

Pasek boczny: Zanim przejdziemy dalej, powiem, że tak, wiem, że odpowiedź brzmi „Call Dispose () Dummy!” ale myślę, że nawet jeśli pozwolisz, aby wszystkie odniesienia wykraczały poza zakres, powinno się zdarzyćostatecznie i garbage collector powinien pozbyć się DataPort

Powrót do wydania: Korzystając z SOS.dll, widzę, że powodem, dla którego mój DataPort nie jest zbierany, jest to, że wątek, który on wywołał, nadal ma odwołanie do obiektu DataPort - poprzez niejawny parametr „this” metody instancji, który wątek biegnie. Działający wątek roboczynie będą zbierane śmieci, więc wszelkie odwołania, które są w zasięgu działającego wątku roboczego, również nie kwalifikują się do zbierania śmieci.

Wątek sam uruchamia zasadniczo następujący kod:

public void WorkerThreadMethod(object unused)
{
  ManualResetEvent dataReady = pInvoke_SubcribeToEvent(this.nativeHardwareHandle);
  for(;;)
  {
    //Wait here until we have data, or we got a signal to terminate the thread because we're being disposed
    int signalIndex = WaitHandle.WaitAny(new WaitHandle[] {this.dataReady, this.closeSignal});
    if(signalIndex == 1) //closeSignal is at index 1
    {
      //We got the close signal.  We're being disposed!
      return; //This will stop the thread
    }
    else
    {
      //Must've been the dataReady signal from the hardware and not the close signal.
      this.ProcessDataFromHardware();
      dataReady.Reset()
    }
  }
}

Metoda Dispose zawiera następujący (odpowiedni) kod:

public void Dispose()
{
  closeSignal.Set();
  workerThread.Join();
}

Ponieważ wątek jest rootem gc i zawiera odwołanie do DataPort, DataPort nigdy nie kwalifikuje się do zbierania śmieci. Ponieważ finalizator nigdy nie jest wywoływany, nigdy nie wysyłamy sygnału zamknięcia do wątku roboczego. Ponieważ wątek roboczy nigdy nie otrzymuje sygnału zamknięcia, cały czas działa i zachowuje to odniesienie. Potwierdź!

Jedyną odpowiedzią na ten problem jest pozbycie się parametru „this” w metodzie WorkerThread (wyszczególnionej poniżej w odpowiedziach). Czy ktoś inny może pomyśleć o innej opcji? Musi istnieć lepszy sposób na stworzenie obiektu za pomocą wątku o takim samym czasie życia obiektu! Alternatywnie, czy można to zrobić bez oddzielnego wątku? Wybrałem ten konkretny projekt na podstawieten post na forach msdn, które opisują niektóre wewnętrzne szczegóły implementacji zwykłej klasy portu szeregowego .NET

Aktualizacja trochę dodatkowych informacji z komentarzy:

Przedmiotowy wątek ma ustawioną wartość IsBackground na trueZasoby niezarządzane wymienione powyżej nie mają wpływu na problem. Nawet jeśli w przykładzie użyto zasobów zarządzanych, nadal będę widział ten sam problem

questionAnswers(2)

yourAnswerToTheQuestion