4

システムの稼働時間を確保するための堅牢な方法が必要で、最終的に次のようなものを使用しました。人々がそれを読むのを助けるためにいくつかのコメントを追加しました。これは.NET 3.5アプリケーションで実行する必要があるため、タスクを使用できません。

// This is a structure, can't be marked as volatile
// need to implement MemoryBarrier manually as appropriate
private static TimeSpan _uptime;

private static TimeSpan GetUptime()
{
    // Try and set the Uptime using per counters
    var uptimeThread = new Thread(GetPerformanceCounterUptime);
    uptimeThread.Start();

    // If our thread hasn't finished in 5 seconds, perf counters are broken
    if (!uptimeThread.Join(5 * 1000))
    {
        // Kill the thread and use Environment.TickCount
        uptimeThread.Abort();
        _uptime = TimeSpan.FromMilliseconds(
            Environment.TickCount & Int32.MaxValue);
    }

    Thread.MemoryBarrier();
    return _uptime;
}

// This sets the System uptime using the perf counters
// this gives the best result but on a system with corrupt perf counters
// it can freeze
private static void GetPerformanceCounterUptime()
{
    using (var uptime = new PerformanceCounter("System", "System Up Time"))
    {
        uptime.NextValue();
        _uptime = TimeSpan.FromSeconds(uptime.NextValue());
    }
}

私が苦労している部分は、どこThread.MemoryBarrier()に配置すればよいですか?値を読み取る前に配置していますが、現在のスレッドまたは別のスレッドが値を書き込んだ可能性があります。上記は正しいように見えますか?

編集、ダニエルに基づく回答

これが私が最終的に実装したものです。両方の洞察に感謝します。

private static TimeSpan _uptime;

private static TimeSpan GetUptime()
{
    var uptimeThread = new Thread(GetPerformanceCounterUptime);
    uptimeThread.Start();

    if (uptimeThread.Join(5*1000))
    {
        return _uptime;
    }
    else
    {
        uptimeThread.Abort();
        return TimeSpan.FromMilliseconds(
            Environment.TickCount & Int32.MaxValue);
    }
}

private static void GetPerformanceCounterUptime()
{
    using (var uptime = new PerformanceCounter("System", "System Up Time"))
    {
        uptime.NextValue();
        _uptime = TimeSpan.FromSeconds(uptime.NextValue());
    }
}

編集 2

Bob のコメントに基づいて更新されました。

private static DateTimeOffset _uptime;

private static DateTimeOffset GetUptime()
{
    var uptimeThread = new Thread(GetPerformanceCounterUptime);
    uptimeThread.Start();

    if (uptimeThread.Join(5*1000))
    {
        return _uptime;
    }
    else
    {
        uptimeThread.Abort();
        return DateTimeOffset.Now.Subtract(TimeSpan.FromMilliseconds(
            Environment.TickCount & Int32.MaxValue));
    }
}

private static void GetPerformanceCounterUptime()
{
    if (_uptime != default(DateTimeOffset))
    {
        return;
    }

    using (var uptime = new PerformanceCounter("System", "System Up Time"))
    {
        uptime.NextValue();
        _uptime = DateTimeOffset.Now.Subtract(
            TimeSpan.FromSeconds(uptime.NextValue()));
    }
}
4

2 に答える 2

2

.NETでのAFAIK書き込みは揮発性であるため、メモリフェンスが必要になるのは、並べ替えやキャッシュの対象となるため、各読み取りの前だけです。ジョー・ダフィーの投稿から引用するには:

参考までに、私ができる限り簡単に述べられているルールを理解するようになったので、ここにルールがあります。

Rule 1: Data dependence among loads and stores is never violated.
Rule 2: All stores have release semantics, i.e. no load or store may move after one.
Rule 3: All volatile loads are acquire, i.e. no load or store may move before one.
Rule 4: No loads and stores may ever cross a full-barrier. 
Rule 5: Loads and stores to the heap may never be introduced.
Rule 6: Loads and stores may only be deleted when coalescing adjacent loads and 
stores from/to the same location.

この定義では、不揮発性負荷に何らかのバリアを関連付ける必要がないことに注意してください。したがって、ロードは自由に並べ替えることができ、書き込みはそれらの後に移動する可能性があります(ただし、ルール2により、前ではありません)。このモデルでは、ルール4で提供される完全なバリアの強度が本当に必要になる唯一の真のケースは、ストアの後に揮発性の負荷が続く場合に再注文を防ぐことです。障壁がなければ、指示は並べ替えることができます。

于 2012-06-19T13:56:49.963 に答える
2

Thread.JoinuptimeThread によって実行された書き込みがメインスレッドで可視であることをすでに保証しています。明示的なメモリ バリアは必要ありません。(によって実行される同期がJoinなければ、書き込み後と読み取り前の両方のスレッドでバリアが必要になります)

ただし、コードには潜在的な問題があります。構造体への書き込みTimeSpanはアトミックではなく、メイン スレッドと uptimeThread が同時に書き込みを行う可能性があります (中止を通知するThread.Abortだけで、スレッドが中止を完了するのを待ちません)。 )、引き裂かれた書き込みを引き起こします。私の解決策は、中止するときにフィールドをまったく使用しないことです。また、複数の同時呼び出しGetUptime()によって同じ問題が発生する可能性があるため、代わりにインスタンス フィールドを使用する必要があります。

private static TimeSpan GetUptime()
{
    // Try and set the Uptime using per counters
    var helper = new Helper();
    var uptimeThread = new Thread(helper.GetPerformanceCounterUptime);
    uptimeThread.Start();

    // If our thread hasn't finished in 5 seconds, perf counters are broken
    if (uptimeThread.Join(5 * 1000))
    {
        return helper._uptime;
    } else {
        // Kill the thread and use Environment.TickCount
        uptimeThread.Abort();
        return TimeSpan.FromMilliseconds(
            Environment.TickCount & Int32.MaxValue);
    }
}

class Helper
{
    internal TimeSpan _uptime;

    // This sets the System uptime using the perf counters
    // this gives the best result but on a system with corrupt perf counters
    // it can freeze
    internal void GetPerformanceCounterUptime()
    {
        using (var uptime = new PerformanceCounter("System", "System Up Time"))
        {
            uptime.NextValue();
            _uptime = TimeSpan.FromSeconds(uptime.NextValue());
        }
    }
}

ただし、パフォーマンス カウンター スレッドの中止が正しく機能するかどうかはわかりませんThread.Abort()。マネージ コードの実行を中止するだけです。コードが Windows API 呼び出し内でハングしている場合、スレッドは実行を続けます。

于 2012-06-19T13:48:53.587 に答える