我需要一种健壮的方法来获得系统正常运行时间,并最终使用如下所示。添加了一些评论来帮助人们阅读它。我不能使用任务,因为这必须在.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()));
}
}发布于 2012-06-19 13:48:53
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调用中,线程将继续运行。
发布于 2012-06-19 13:56:49
AFAIK在.NET中的写入是不稳定的,所以您需要内存围栏的唯一地方是在每次读取之前,因为它们需要重新排序和/或缓存。引用乔·达菲的帖子
作为参考,以下是我所理解的规则,我尽可能简单地理解它们: 规则1:负载和存储之间的数据依赖从未被违反。规则2:所有存储都具有发布语义,即没有加载或存储可以在一次之后移动。规则3:所有易失性负载都被获取,即没有负载或存储可以在一个负载之前移动。规则4:任何货物和仓库都不可能越过全障碍。规则5:堆中的负载和存储可能永远不会被引入。规则6:只有在将相邻的负载和存储从/到同一位置合并时,才能删除负载和存储。 注意,根据这个定义,非易失性负载不需要有任何与它们相关的屏障。因此,加载可以自由地重新排序,并且写入可以在它们之后移动(尽管在此之前没有,因为规则2)。有了这个模型,唯一真正需要规则4所提供的全屏障强度的情况是在存储后面跟着易失性负载的情况下防止重新排序。如果没有障碍,指令可能会重新排序。
https://stackoverflow.com/questions/11102123
复制相似问题