Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/315.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 系统正常运行时间&;记忆载体_C#_Multithreading_Memory Barriers - Fatal编程技术网

C# 系统正常运行时间&;记忆载体

C# 系统正常运行时间&;记忆载体,c#,multithreading,memory-barriers,C#,Multithreading,Memory Barriers,我需要一种健壮的方法来获得系统正常运行时间,并最终使用了以下内容。 添加了一些注释以帮助人们阅读。我不能使用任务,因为它必须在.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 Ge

我需要一种健壮的方法来获得系统正常运行时间,并最终使用了以下内容。 添加了一些注释以帮助人们阅读。我不能使用任务,因为它必须在.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()));
    }
}

Thread.Join
已确保uptimeThread执行的写入在主线程上可见。你不需要任何明确的记忆障碍。(如果没有由
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调用中,线程将继续运行。

AFAIK在.NET中的写入是不稳定的,因此您需要内存隔离的唯一位置是在每次读取之前,因为它们需要重新排序和/或缓存。引自:

以下是我理解的规则,仅供参考 尽可能简单地说:

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规定的完整屏障的强度是为了防止 在存储之后是易失性负载的情况下重新排序。 如果没有屏障,说明可能会重新排序


你能解释一下为什么Thread.Join()会保证一个处理器上完成的写入被另一个处理器的读取看到吗?同意问题是字段,如果没有.NET 3.5要求,我会将其作为任务实现。@M Afifi:
Thread.Join
在调用时带有隐式内存障碍。通常,等待操作(例如线程退出)在另一个线程上发生的操作,还要确保在等待完成后,操作之前发生的所有内存写入都可见。“等到X完成”如果不是意味着“等到我看到X的结果”的话,那将是非常无用的。不幸的是,Microsoft.NET API文档没有明确提到内存顺序效应。(Java文档在这方面要好得多)存储在.NET CLR实现上是隐式易变的(您发布的规则不受规范的保证!)在这里没有帮助,因为存储不是原子的。@Daniel:因为OP讨论的是易失性/内存围栏,我觉得他只是在问可见性,而不是原子性。问的问题是“这个代码正确吗?”,没有原子性的可见性在那里是无用的。你是说性能计数器会在应用程序的生命周期内损坏吗?这就是为什么你每次都要开始检查新的线?@Bobb,嗯,我不确定。我的应用程序是在机器启动时启动的windows服务。我看到它已损坏,我的应用程序始终运行。在某些机器上,它很可能在应用程序的生命周期内发生。PS,我很感激我可以做一些缓存,添加DateTime。现在作为一个成员变量,如果它们都不是null,则计算新的正常运行时间值。可能会更改代码以确保不会产生潜在负载的“不可分配”线程。创建和启动线程只是为了获取计时器值是错误的,我甚至不知道从哪里开始。你应该以某种方式在开始时拍摄一次正常运行时间的快照(我不能评论你的“腐败”概念)和使用高分辨率时钟的本地时间(谷歌上有很多例子)。在每个GetUpTime上,只需返回initialUpTime+(timeNow-timeAtUpTimeSnapshot)。这个调用只需要几行代码,是同步的、可预测的和可管理的。。。你现在所做的毫无意义。@Bobb我同意每次开始一根线都是懒惰的。酌情更新。但是第一次运行时我确实需要一个单独的线程。尽管令人钦佩,正常运行时间是误导性的,更像是上次启动时间。只需锁定正常运行时间(内部使用2行代码)。你不需要记忆障碍。