System.Timers.Timer vs System.Threading.Timer

564

Ostatnio sprawdzałem niektóre z możliwych timerów System.Threading.TimeriSystem.Timers.Timer są to te, które wydają mi się potrzebne (ponieważ obsługują pule wątków).

Tworzę grę i planuję używać wszelkiego rodzaju wydarzeń, w różnych odstępach czasu itp.

Który byłby najlepszy?

TheAJ
źródło

Odpowiedzi:

362

Ten artykuł zawiera dość wyczerpujące wyjaśnienie:

Porównywanie klas timerów w bibliotece klas .NET Framework ” - dostępny również jako plik .chm

Wydaje się, że szczególna różnica System.Timers.Timerpolega na aplikacjach wielowątkowych i dlatego jest bezpieczna dla wątków dzięki swojej SynchronizationObjectwłaściwości, podczas gdy System.Threading.Timerironicznie nie jest bezpieczna dla wątków od razu po wyjęciu z pudełka.

Nie wierzę, że istnieje różnica między nimi, ponieważ dotyczy to, jak małe mogą być twoje interwały.

David Andres
źródło
69
Myślę, że ten fragment jest pouczający: „W przeciwieństwie do System.Windows.Forms.Timer, klasa System.Timers.Timer domyślnie wywołuje procedurę obsługi zdarzeń timera w wątku roboczym uzyskanym z puli wątków środowiska uruchomieniowego języka wspólnego (CLR) . [...] Klasa System.Timers.Timer zapewnia łatwy sposób poradzenia sobie z tym dylematem - udostępnia publiczną właściwość SynchronizingObject. Ustawienie tej właściwości na wystąpienie formularza Windows (lub kontrolki w formularzu Windows) spowoduje upewnij się, że kod w module obsługi zdarzeń Elapsed działa w tym samym wątku, w którym utworzono instancję obiektu SynchronizingObject. ”
mico,
7
Według wątku sekcji Bezpieczeństwo w Threading.Timer„s artykule MSDN , jest całkowicie bezpieczny wątku ...
Pieter
62
System.Threading.Timerjest tak „ironicznie”, że nie jest wątkowo bezpieczny, System.Threading.Threada nici uzyskane przez pulę. Tylko dlatego, że klasy te nie trzymają ręki i locksame zarządzają użyciem słowa kluczowego, nie oznacza, że ​​te klasy nie są bezpieczne dla wątków. Równie dobrze można powiedzieć, że System.Threading.Threadnie jest wątkowo bezpieczny, ponieważ jest dokładnie tak samo.
Kirk Woll
8
Interwał System.Timer.Timer może być tylko Int32 System.Threading.Timer interwał może wynosić do Int64
Brent
11
To niefortunne, że ta wysoce myląca (w najlepszym razie) odpowiedź została zaakceptowana i tak wysoko oceniona. Jedyne istotne stwierdzenie w samej odpowiedzi jest po prostu błędne. Nie SynchronizingObjectpowoduje, że sam obiekt timera jest bezpieczny dla wątków. Po prostu upewnia się, że Twój kod do obsługi zdarzenia licznika czasu jest wywoływany w określonym wątku (jeśli odpowiednio ustawisz tę właściwość). Sam obiekt timera nie jest gwarantowany pod kątem bezpieczeństwa wątków, jak to wyraźnie zaznaczono w dokumentacji. Z drugiej strony System.Threading.Timerobiekt jest specjalnie udokumentowany jako bezpieczny dla wątków.
Peter Duniho,
169

System.Threading.Timerjest zwykłym zegarem. Wywołuje cię z powrotem w wątku puli wątków (z puli procesów roboczych).

System.Timers.Timerjest to, System.ComponentModel.Componentktóre otacza a System.Threading.Timer, i zapewnia pewne dodatkowe funkcje używane do wysyłania w określonym wątku.

System.Windows.Forms.Timerzamiast tego pakuje natywny HWND tylko do wiadomości i używa Timerów Windows do wywoływania zdarzeń w tej pętli komunikatów HWND.

Jeśli Twoja aplikacja nie ma interfejsu użytkownika, a chcesz jak najlżejszy i najbardziej uniwersalny zegar .Net (ponieważ z przyjemnością wymyślasz własne wątki / wysyłanie) System.Threading.Timer jest tak dobry, jak to możliwe w środowisku.

Nie jestem do końca jasne, z jakimi rzekomymi problemami System.Threading.Timersą „nie wątkowe” . Być może jest to to samo, co zadane w tym pytaniu: Bezpieczeństwo wątków System.Timers.Timer vs. System.Threading.Timer , a może wszyscy po prostu oznaczają, że:

  1. łatwo jest pisać warunki wyścigu, korzystając z liczników czasu. Np. Zobacz to pytanie: Bezpieczeństwo wątków Timer (System.Threading)

  2. ponowne wejście powiadomień timera, w którym zdarzenie timera może się uruchomić i oddzwonić po raz drugi, zanim zakończysz przetwarzanie pierwszego zdarzenia. Np. Patrz pytanie: Bezpieczne wykonywanie wątków za pomocą System.Threading.Timer i Monitor

Tim Lovell-Smith
źródło
Prawda System.Timers.Timerzastosowania System.Threading.Timerwewnętrznie. Zobacz kod źródłowy .
stomy
120

W swojej książce „ CLR Via C #Jeff Ritcher odradza korzystanie z System.Timers.Timertego timera System.ComponentModel.Component, co pozwala na jego wykorzystanie w powierzchni projektowej Visual Studio. Przydałoby się to tylko wtedy, gdy chcesz mieć zegar na powierzchni projektowej.

Preferuje System.Threading.Timerwykonywanie zadań w tle w wątku puli wątków.

Bohater
źródło
36
Może być stosowany na powierzchni projektowej - nie oznacza to, że musi być, i nie ma negatywnych skutków z tego powodu. Czytając artykuł we wcześniejszej odpowiedzi na to pytanie, Timers.Timer wydaje się znacznie bardziej preferowany niż Threading.Timer.
Stephen Drew
6
Co jest lepsze, zależy od kontekstu, prawda? Jak rozumiem, System.Threading.Timer wykonuje przekazane wywołanie zwrotne dla nowego wątku roboczego z ThreadPool. Zakładam również, że nie zawsze jest to bezpieczne dla wątków. Trochę ma to sens. Teoretycznie nie musiałbyś się martwić o cholerstwo, które spowodowało rozwinięcie własnego wątku roboczego, ponieważ ten licznik zrobi to za Ciebie. Trochę to wydaje się absurdalnie przydatne.
Finster
6
Korzystanie System.Threading.Timerjest podobne do korzystania z puli wątków lub tworzenia własnego wątku. Z Oczywiście te zajęcia nie obsługuje synchronizacji dla ciebie - to twoja praca! Ani wątek puli wątków, twój własny wątek, ani wywołanie zwrotne licznika czasu nie poradzą sobie z blokowaniem - na jakim obiekcie, w jakiej modzie iw jakich okolicznościach musisz zablokować, wymaga dobrej oceny, a wersja licznika wątków zapewnia największą elastyczność i ziarnistość.
Kirk Woll
2
-1 ta odpowiedź jest subiektywna lub wyrażana od samego początku i nie zawiera konkretnych informacji na temat tego, dlaczego System.Threading.Timer jest preferowany przez Jeffa Ritchera
Brian Ogden
42

Informacje od Microsoft na ten temat (patrz Uwagi na MSDN ):

  • System.Timers.Timer , który uruchamia zdarzenie i wykonuje kod w co najmniej jednym zdarzeniu, w regularnych odstępach czasu. Klasa jest przeznaczona do użycia jako komponent serwerowy lub usługowy w środowisku wielowątkowym; nie ma interfejsu użytkownika i nie jest widoczny w czasie wykonywania.
  • System.Threading.Timer , który wykonuje pojedynczą metodę wywołania zwrotnego w wątku puli wątków w regularnych odstępach czasu. Metoda wywołania zwrotnego jest definiowana, gdy licznik czasu jest tworzony i nie można go zmienić. Podobnie jak klasa System.Timers.Timer, ta klasa jest przeznaczona do użycia jako komponent serwerowy lub usługowy w środowisku wielowątkowym; nie ma interfejsu użytkownika i nie jest widoczny w czasie wykonywania.
  • System.Windows.Forms.Timer (tylko .NET Framework), składnik Windows Forms, który uruchamia zdarzenie i wykonuje kod w jednym lub kilku obiektach przechwytujących w regularnych odstępach czasu. Komponent nie ma interfejsu użytkownika i jest przeznaczony do użytku w środowisku jednowątkowym; wykonuje się w wątku interfejsu użytkownika.
  • System.Web.UI.Timer (tylko .NET Framework), komponent ASP.NET, który wykonuje asynchroniczne lub synchroniczne zwrotne strony WWW w regularnych odstępach czasu.

Warto o tym wspomnieć System.Timers.Timer został wycofany z .NET Core 1.0, ale został ponownie zaimplementowany w .NET Core 2.0 (/ .NET Standard 2.0). Celem .NET Standard 2.0 było, aby przejście z systemu .NET Framework było jak najłatwiejsze, co prawdopodobnie jest przyczyną jego powrotu.

Kiedy było przestarzałe, zalecany był dodatek .NET Portability Analyzer Visual Studio Add-InSystem.Threading.Timer .

Wygląda na to, że Microsoft preferuje System.Threading.Timerwcześniej System.Timers.Timer.

INFORMACJA O EDYCJI 15.11.2018: Zmieniam rękę, ponieważ stare informacje o .NET Core 1.0 nie były już aktualne.

ice1e0
źródło
2
Wygląda na to, że to prawda: github.com/npgsql/npgsql/issues/471#issuecomment-94104090
Marco Sulla
@Taegost, jego użycie jest również ograniczone - zobacz MSDN msdn.microsoft.com/en-us/library/... i przeczytaj uwagi na temat dostępności platformy.
astrowalker
@astrowalker - Dziękuję za to, że w momencie, gdy robiłem komentarz, ta odpowiedź nie miała prawie żadnych szczegółów. Ponieważ ma teraz szczegóły, o które pytałem, usunąłem swój komentarz.
Taegost
1
System.Timers.Timer jest teraz obsługiwany w .NET Standard 2.0 i .NET Core 2.0 i nowszych. docs.microsoft.com/en-us/dotnet/api/system.timers.timer (przewiń do końca artykułu)
Lee Grissom,
Istnieje również System.Windows.Threading.DispatcherTimer dla tej odpowiedzi .
SpeedCoder5
39

Jedną ważną, nie wymienioną powyżej różnicą, która może cię złapać, jest to, że System.Timers.Timercicho połyka wyjątki, podczas System.Threading.Timergdy nie.

Na przykład:

var timer = new System.Timers.Timer { AutoReset = false };
timer.Elapsed += (sender, args) =>
{
    var z = 0;
    var i = 1 / z;
};
timer.Start();

vs

var timer = new System.Threading.Timer(x =>
{
    var z = 0;
    var i = 1 / z;
}, null, 0, Timeout.Infinite);
stovroz
źródło
1
Niedawno natrafiłem na ten problem z Timers.Timer i było to bardzo bolesne ... Jakieś pomysły, jak mogę przepisać z Threading.Timer? stackoverflow.com/questions/41618324/…
Tez Wingfield
7
W kodzie źródłowym jest pusty haczyk. Zobacz kod źródłowy System.Timers.Timer tutaj
stomy
Omgsh, dlaczego programiści MS nie są w stanie robić tych samych rzeczy naprawdę tak samo?
xmedeko
2
Przykład nie wyjaśnia, w jaki sposób jeden połyka wyjątki, a drugi nie. Czy ktoś mógłby wypełnić dane?
Sean
24

Znalazłem krótkie porównanie z MSDN

Biblioteka klas .NET Framework zawiera cztery klasy o nazwie Timer, z których każda oferuje inną funkcjonalność:

System.Timers.Timer, który uruchamia zdarzenie i wykonuje kod w jednym lub kilku obiektach przechwytujących w regularnych odstępach czasu. Klasa jest przeznaczona do użycia jako komponent serwerowy lub usługowy w środowisku wielowątkowym; nie ma interfejsu użytkownika i nie jest widoczny w czasie wykonywania.

System.Threading.Timer, która wykonuje pojedynczą metodę wywołania zwrotnego w wątku puli wątków w regularnych odstępach czasu. Metoda wywołania zwrotnego jest definiowana, gdy licznik czasu jest tworzony i nie można go zmienić. Podobnie jak klasa System.Timers.Timer, ta klasa jest przeznaczona do użycia jako komponent serwerowy lub usługowy w środowisku wielowątkowym; nie ma interfejsu użytkownika i nie jest widoczny w czasie wykonywania.

System.Windows.Forms.Timer, składnik Windows Forms, który uruchamia zdarzenie i wykonuje kod w jednym lub kilku obiektach przechwytujących w regularnych odstępach czasu. Komponent nie ma interfejsu użytkownika i jest przeznaczony do użytku w środowisku jednowątkowym.

System.Web.UI.Timer, komponent ASP.NET, który regularnie wykonuje asynchroniczne lub synchroniczne aktualizacje strony internetowej.

Domyślna
źródło
1

Te dwie klasy są funkcjonalnie równoważne, z wyjątkiem tego, że System.Timers.Timerma opcję wywoływania wszystkich wywołań zwrotnych upływu czasu przez ISynchronizeInvoke poprzez ustawienie SynchronizingObject . W przeciwnym razie oba liczniki wywołują wywołania zwrotne utraty ważności w wątkach puli wątków.

Podczas przeciągania znaku System.Timers.Timerna powierzchnię projektu Windows Forms Visual Studio ustawia SynchronizingObject na obiekt formularza, co powoduje, że wszystkie wywołania zwrotne wygasania są wywoływane w wątku interfejsu użytkownika.

Edward Brey
źródło
1

Z MSDN: System.Threading.Timerto prosty, lekki licznik czasu, który wykorzystuje metody wywołania zwrotnego i jest obsługiwany przez wątki puli wątków. Nie zaleca się używania go z Windows Forms, ponieważ jego wywołania zwrotne nie występują w wątku interfejsu użytkownika. System.Windows.Forms.Timerjest lepszym wyborem do użycia z Windows Forms. W przypadku funkcji czasomierza opartej na serwerze można rozważyć użycie System.Timers.Timer, która wywołuje zdarzenia i ma dodatkowe funkcje.

Źródło

Greg Gum
źródło