4

C#タイマーオブジェクトは、経過時間がいつ発生したかをどのように決定しますか?

単純にループするのか、それともこれよりもインテリジェントなのか疑問に思っています。

また、C# のソース コードがどこにあるかを知っておくとよいでしょう。

4

2 に答える 2

7

CLR は、アプリケーションで作成したすべてのタイマー オブジェクトを処理する専用スレッドを作成し、それらの Elapsed イベントとコールバック ハンドラーを実行します。デバッガーで使用されていることがわかります。次のようなコンソール モード アプリから始めます。

using System;
using System.Timers;

class Program {
    static void Main(string[] args) {
        var t = Timer();
        t.Elapsed += ElapsedEventHandler((s, e) => { });
        t.Start();
    }
}

プロジェクト + プロパティ、[デバッグ] タブで、[ネイティブ コードのデバッグを有効にする] (別名アンマネージ コード) オプションにチェックマークを付けます。ツール + オプション、デバッグ、シンボルを開き、Microsoft シンボル サーバーが有効になっていることを確認します。F11 を押してデバッグを開始します。

ここで、Debug + Windows + Threads を使用します。4 つのスレッドが一覧表示されます。メイン スレッド、ファイナライザー スレッド、デバッグ スレッド、アイドル スレッドプール スレッド。t.Start() メソッド呼び出しを通り過ぎるまでステップを続けます。新しいスレッドが追加されたことに注意してください。名前は「ThreadPoolMgr::TimerThreadStart」です。それをダブルクリックして、[コール スタック] ウィンドウを確認します。わかるでしょ:

ntdll.dll!_NtDelayExecution@8()  + 0x15 bytes   
ntdll.dll!_NtDelayExecution@8()  + 0x15 bytes   
KernelBase.dll!_SleepEx@8()  + 0x39 bytes   
clr.dll!ThreadpoolMgr::TimerThreadFire()  + 0x3e bytes  
clr.dll!ThreadpoolMgr::TimerThreadStart()  + 0x6a bytes 
kernel32.dll!@BaseThreadInitThunk@12()  + 0x12 bytes    
ntdll.dll!___RtlUserThreadStart@8()  + 0x27 bytes   
ntdll.dll!__RtlUserThreadStart@8()  + 0x1b bytes

ここで重要なのは TimerThreadStart() 関数です。これは、CLR が開始したスレッドの開始点です。そして、SleepEx() 呼び出しは、次のタイマーが期限切れになるまでスリープしているこのスレッドです。このスタック トレースは .NET 4.5 用であり、.NET 2.0 以降ずっとこのように一貫しています。利用可能なソース コードについては、こちらからダウンロードできる SSCLI20 ソース コードを参照してください。そのコードを検索すると、clr/src/vm/win32threadpool.cpp ソース コード ファイルに移動します。何が起こっているかを確認するためにそれを見てください。

簡単に説明します。タイミング ソースは GetTickCount() API 関数であり、Environment.TickCount で使用されるものと同じです。FireTimers() 関数は、アクティブなタイマーのどれが最初に期限切れになるかを分類します。SleepEx() 関数は、アラート可能であることを除いて、 Thread.Sleep () と同じです。スリープが完了する前に、APC (非同期プロシージャ コール) によって中断することができます。同じファイルで使用されている QueueUserAPC() 関数は、プログラムがシャットダウンしているためにスレッドを終了する必要がある場合、および新しいスリープを計算する必要があるようにタイマーが追加または変更された場合に使用されます。

于 2012-10-29T16:25:04.457 に答える
4

.NET BCL には、タイマーを提供する少なくとも 2 つのクラスがあります。

ただし、2 番目のクラスは最初のクラスを使用して実装されます。最初のクラスはある種のネイティブ Win32 タイマーを使用しており、Hans Passant は使用されているメカニズムに関する詳細情報を提供しています。BCL ソース コードでは、タイマーは という名前の内部クラスを使用して実装されていTimerQueueます。クラスの実装にTimerQueueは次のコメントがあります。

TimerQueue は、この AppDomain でアクティブなタイマーのリストを維持します。VM によって提供される単一のネイティブ タイマーを使用して、AppDomain 内のすべてのマネージド タイマーをスケジュールします。

したがって、基本的に AppDomain には、アンマネージ コードを使用して実装された単一のタイム ソースがあります。詳細については、Hans Passant の回答を参照してください。


また、C# のソース コードがどこにあるかを知っておくとよいでしょう。

私はJetBrains の dotPeekを使用しています。ソース ファイルが存在する場合は、Microsoft からソース ファイルを取得するように構成できます。調べたい .NET のバージョンをロードし、Ctrl+を押しTて「Timer」と入力し、必要な特定のクラスを選択するだけで、ソースが存在する場合はダウンロードされます。それ以外の場合は、逆コンパイルされたバージョンが表示されます (構成によって異なります)。

このツールでは、マネージ コードの逆コンパイルのみが可能であり、.NET でのタイマーの実装を完全に理解するには、アンマネージ コードをさらに詳しく調べる必要があることに注意してください。

于 2012-10-29T15:42:25.780 に答える