Pytanie W języku C #, gdzie powinienem zachować referencję czasomierza?


Dokumentacja System.Threading.Timer mówi, że powinienem zachować dla niego odnośnik, aby uniknąć gromadzenia śmieci. Ale gdzie powinienem to zrobić? Mój main jest bardzo proste, że nie wiem, gdzie zachować referencję:

class Program {
    static void Main() {
        new System.Threading.Thread(myThreadStart).Start();
        new System.Threading.Timer(myTimerCallback, new MyStateObject(), 0, 5000);
    }
}

Myślałem o zachowaniu odniesienia w static pole w Program klasa, zakładając, że static pola nie są zbierane do końca aplikacji. Ale nie jestem pewien, czy to najlepszy sposób na zrobienie tego, więc będę wdzięczny za twoją radę.


14
2018-01-25 07:57


pochodzenie




Odpowiedzi:


Jeśli twój Timer jest obiektem na poziomie aplikacji, nie ma nic złego w uczynieniu go statycznym członkiem twojej głównej klasy. I tak też bym to zrobił.


18
2018-01-25 08:58



+1 Jest to najbardziej oczywiste i najprostsze rozwiązanie. - Joe
Czy możesz podać próbkę w swojej odpowiedzi - variable
Również zadeklarowanie czasomierza w zakresie klasy uniemożliwi GC, nie jest to? - variable


EDYCJA: Moja oryginalna odpowiedź to śmieci. Naprawdę śmieci. Zachowałem to tutaj, żeby wyjaśnić czemu to śmieci - są w komentarzach, ale zostałyby usunięte z odpowiedzią.

GC.KeepAlive zapewnia tylko, że odniesienie jest traktowane jako root, aż do zakończenia połączenia. W kodzie na dole tej odpowiedzi metoda GC.KeepAlive byłaby wywoływana natychmiast, a następnie licznik czasu nadal kwalifikowałby się do zbierania śmieci. Ponieważ nowo utworzony wątek jest wątkiem pierwszoplanowym, aplikacja będzie działać tak długo, jak długo będzie żyła (podczas gdy program używa wątku tła, co nie przeszkadza w wyjściu programu). Oznacza to, że główna metoda kończy się, ale aplikacja musi nadal działać.

Prawdopodobnie a prostsze rozwiązanie byłoby uruchomić myThreadStart w głównym wątku, zamiast tworzyć nowy, a następnie pozwolić głównym wątkom umrzeć. Innymi słowy, prostym rozwiązaniem byłoby:

using System.Threading;

class Program {
    static void Main() {
        Timer timer = new Timer(myTimerCallback, 
                                new MyStateObject(), 0, 5000);
        myThreadStart();
        GC.KeepAlive(timer);
    }
}

Zakładam real kod jest jednak bardziej skomplikowany - w takim przypadku użycie prywatnej zmiennej statycznej sugerowanej w innych odpowiedziach jest prawdopodobnie drogą do zrobienia. To naprawdę będzie zależeć od użycia. Osobiście wolę nie tworzyć pola statycznego tylko po to, aby zapobiec gromadzeniu czegoś, jeśli istnieje alternatywa (jak wyżej), ale czasami jest to praktycznie jedyny sposób na zrobienie tego.

Oryginalna (zła) odpowiedź:

Jeśli naprawdę chcesz go przydzielić w Main, możesz użyć GC.KeepAlive:

using System.Threading;

class Program {
    static void Main() {
        new Thread(myThreadStart).Start();
        Timer timer = new Timer(myTimerCallback, 
                                new MyStateObject(), 0, 5000);
        GC.KeepAlive(timer);
    }
}

12
2018-01-25 07:59



Dzięki. Zgodnie z dokumentacją KeepAlive, "odwołuje się do określonego obiektu, co sprawia, że ​​nie kwalifikuje się do usuwania śmieci od początku bieżącej procedury do punktu, w którym wywoływana jest ta metoda." Czy nie oznacza to, że pod koniec głównej metody obiekt może być GC'd? - Hosam Aly
@Hosam - w każdym razie musisz zachować swoją metodę Main () - powrócenie z niej spowoduje przerwanie twojego programu. - Michael Burr
@ Michael Burr, program nie wyjdzie z wątku whild utworzonego przez (new Tread ...) jest uruchomiony. - aku
@Michael, tak masz rację, zauważyłem, że po napisałem pytanie. @aku, masz rację, właśnie tego wypróbowałem. Dzięki wszystkim. - Hosam Aly
@Hosam Aly, myślę, że powinienem przeczytać FAQ :) - aku


Myślę, że to jest w porządku, aby zachować prywatne statyczne pole twojej klasy.

Zachowałbym to odniesienie jako pole statyczne, zamiast grać za pomocą garbage collectora.


4
2018-01-25 08:01



Dzięki. Przypuszczam, że śmieciarz nie mógłby zebrać statycznego pola, nawet jeśli byłby prywatny (i nie byłby już dostępny), prawda? - Hosam Aly
utrzyma to przy życiu - aku
zasadniczo dwie metody prowadzą do podobnego wyniku - odwołanie do licznika czasu nie zostanie zebrane podczas pracy programu (np. wątek utworzony za pomocą nowego wątku ... jest żywy). - aku
Myślę, że możesz chcieć usunąć "GC.KeepAlive" z odpowiedzi po wyjaśnieniu Jona Skeeta. :) - Hosam Aly