2008-08-28 8 views
26

Ich schreibe eine App, die Timer s verwenden muss, aber möglicherweise sehr viele von ihnen. Wie skalierbar ist die Klasse System.Threading.Timer? Die Dokumentation sagt nur, dass es "leicht" ist, erklärt es aber nicht weiter. Werden diese Timer in einen einzelnen Thread (oder einen sehr kleinen Threadpool) gesaugt, der alle Callbacks im Auftrag eines Timer verarbeitet, oder hat jedes Timer seinen eigenen Thread?Wie skalierbar ist System.Threading.Timer?

Ich denke, ein anderer Weg, um die Frage neu zu formulieren ist: Wie wird System.Threading.Timer implementiert?

Antwort

29

Ich sage dies als Reaktion auf eine Menge Fragen: Vergessen Sie nicht, dass der (verwaltete) Quellcode für das Framework verfügbar ist. Sie können dieses Tool verwenden, um es zu bekommen: Leider http://www.codeplex.com/NetMassDownloader

, in diesem speziellen Fall eine Menge von der Implementierung in nativen Code, so dass Sie nicht um es zu betrachten bekommen ...

Sie verwende definitiv Pool-Threads und nicht einen Thread pro Timer.

Der Standard Weg, um eine große Sammlung von Timern zu implementieren (was der Kernel tut es intern, und ich würde vermuten, ist indirekt, wie Ihre große Sammlung von Timern endet) ist die Liste nach Zeit sortiert - bis - Ablauf - das System muss sich also nur darum kümmern, den nächsten Timer zu prüfen, der abläuft, nicht die ganze Liste.

Grob gesagt gibt dies O (log n) zum Starten eines Timers und O (1) zum Verarbeiten von laufenden Timern.

Bearbeiten: Just in Jeff Richters Buch gesucht. Er sagt (von Threading.Timer), dass es einen einzigen Thread für alle Timer-Objekte verwendet, dieser Thread weiß, wann der nächste Timer (d. H. Wie oben) fällig ist, und ruft ThreadPool.QueueUserWorkItem für die Rückrufe entsprechend auf. Dies hat zur Folge, dass, wenn Sie die Bearbeitung eines Callbacks für einen Timer nicht beenden, bevor der nächste fällig ist, dass Ihr Callback erneut in einem anderen Poolthread eintritt. Zusammenfassend bezweifle ich, dass Sie ein großes Problem mit vielen Timern sehen werden, aber Sie könnten die Erschöpfung des Thread-Pools erleiden, wenn eine große Anzahl von ihnen auf denselben Timer feuert und/oder ihre Callbacks langsam laufen.

+0

Eine Prioritätswarteschlange wäre wahrscheinlich effizienter als eine sortierte Liste, es sei denn, alle Timer werden am Anfang in Bulk hinzugefügt, dann sortiert und später nicht mehr hinzugefügt. – RAL

+0

Sicher - "eine nach Zeit bis zum Ablauf sortierte Liste" könnte sicherlich eine Art Prioritätswarteschlange sein - ich wollte damit nicht "eine Liste implizieren, über die eine Sortieroperation ausgeführt wurde" –

+1

Ich habe gerade einige Zeit damit verbracht über den Code in der SSCI. Beachten Sie, dass sich der .NET ThreadPool seit der Veröffentlichung von Rotor immens verändert hat. Daher ist es durchaus möglich, dass sich auch der System.Threading.Timer immens verändert hat. Tatsächlich wurden Timer in .NET 1.1 schlecht gebrochen und nur in .NET 2.0 behoben. Wie auch immer, in Rotor werden die Timer in einer verketteten Liste gehalten und durch einen dedizierten Timer ausgelöst. Es gibt einen Timer-Thread für die gesamte Laufzeit (auch über mehrere App-Domänen hinweg). –

7

Ich denke, Sie möchten vielleicht Ihr Design überdenken (das heißt, wenn Sie die Kontrolle über das Design selbst haben). Wenn Sie so viele Timer verwenden, dass dies tatsächlich ein Problem für Sie ist, gibt es dort ein gewisses Potenzial für eine Konsolidierung.

Hier ist ein guter Artikel von MSDN Magazine von vor ein paar Jahren, die die drei zur Verfügung stehenden Timer-Klassen vergleicht, und gibt einen Einblick in ihre Implementierungen:

http://msdn.microsoft.com/en-us/magazine/cc164015.aspx

0

^^ wie DannySmurf sagt: Konsolidieren Sie sie. Erstellen Sie einen Timer-Dienst und fragen Sie nach den Timern. Es muss nur einen aktiven Timer (für den nächsten fälligen Anruf) und einen Verlauf aller Timer-Anfragen behalten und dies über AddTimer()/RemoveTimer() neu berechnen.

5

Konsolidieren Sie sie. Erstellen Sie einen Timer Service und fragen Sie, dass für die Timer. Es wird nur 1 aktiv Timer halten müssen (für den nächsten fälligen Anruf) ...

Für dies eine Verbesserung um mehr als nur eine Menge Threading.Timer Objekte erstellen, müssen Sie davon ausgehen, dass es isn Was Threading.Timer eigentlich schon intern macht.Es würde mich interessieren zu erfahren, wie Sie zu diesem Schluss gekommen sind (ich habe die nativen Teile des Frameworks nicht auseinander genommen, also könnten Sie recht haben).