The StopWatch class isn't inherently thread-safe. Are you
synchronizing access to it appropriately. Can you post some code
demonstrating the problem?
Right. The StopWatch class expects that the Reset/Start/Stop/Elapsed*
methods are all accessed from the same thread. Not only that, they all
have to be accessed from the same CPU. Does StopWatch use the
BeginThreadAffi nity internally to make this happen? I'll assume that
it does. The problem is that if you need to call Reset in one thread
and Elapsed in another, this is very difficult. I did manage to make a
class to do it, however. Here it is for your critique:
using System;
using System.Runtime. InteropServices ;
using System.Diagnost ics;
using System.Threadin g;
namespace Blah
{
/// <summary>
/// Represents a high-resolution stopwatch that is thread-safe and
CPU safe.
/// </summary>
public sealed class StopWatch
{
static private Thread _backgroundThre ad = null;
static private EventWaitHandle _tickCountUpdat eRequest = new
EventWaitHandle (false, EventResetMode. AutoReset);
static private EventWaitHandle _tickCountUpdat eDone = new
EventWaitHandle (false, EventResetMode. AutoReset);
static public long Frequency = 0;
static public long CurrentTickCoun t = 0;
public long StartTickCount = 0;
/// <summary>
/// A thread to make sure CPU tick counts are always read from the
same CPU
/// </summary>
private static void _backgroundThre adFunc()
{
Thread.BeginThr eadAffinity();
Frequency = System.Diagnost ics.Stopwatch.F requency;
while (true)
{
_tickCountUpdat eRequest.WaitOn e();
CurrentTickCoun t = System.Diagnost ics.Stopwatch.G etTimestamp();
_tickCountUpdat eDone.Set();
}
//Thread.EndThrea dAffinity(); // never called
}
/// <summary>
/// The System.Diagnost ics.Stopwatch (and hence, performance
counters) get their tick marks from the CPU that the thread is on. We
create a thread to make sure the tick counts are always read on the
same CPU.
/// </summary>
static StopWatch() {
_backgroundThre ad = new Thread(new
ThreadStart(_ba ckgroundThreadF unc));
_backgroundThre ad.IsBackground = true;
_backgroundThre ad.Start();
}
/// <summary>
/// Initializes a new instance of the StopWatch class.
/// </summary>
/// <exception cref="NotSuppor tedException">T he system does not have
a high-resolution performance counter.</exception>
public StopWatch()
{
Reset();
}
/// <summary>
/// Resets the stopwatch. This method should be called when you
start measuring.
/// </summary>
/// <exception cref="NotSuppor tedException">T he system does not have
a high-resolution performance counter.</exception>
public void Reset()
{
lock (_backgroundThr ead)
{
StopWatch._tick CountUpdateRequ est.Set();
StopWatch._tick CountUpdateDone .WaitOne();
StartTickCount = CurrentTickCoun t;
}
}
/// <summary>
/// Peg the processor for this many seconds; mainly used for testing
/// </summary>
/// <param name="seconds"> </param>
public static void BusyLoop(double seconds)
{
DateTime endTime = DateTime.Now.Ad dSeconds(second s);
int i = 0;
while (DateTime.Now < endTime) { i++; }
}
public long GetCurrentTime_ ms()
{
lock (_backgroundThr ead)
{
StopWatch._tick CountUpdateRequ est.Set();
StopWatch._tick CountUpdateDone .WaitOne();
return CurrentTickCoun t * 1000 / StopWatch.Frequ ency;
}
}
/// <summary>
/// Get the time elapsed, in seconds, since the last Reset() or
since
/// creation if Reset() hasn't been called since then.
/// </summary>
/// <returns>Elapse d time in seconds.</returns>
public double GetElapsed_s()
{
lock (_backgroundThr ead)
{
StopWatch._tick CountUpdateRequ est.Set();
StopWatch._tick CountUpdateDone .WaitOne();
return (double)(Curren tTickCount - StartTickCount) /
(double)StopWat ch.Frequency;
}
}
/// <summary>
/// Get the time elapsed, in milliseconds, since the last Reset() or
since
/// creation if Reset() hasn't been called since then.
/// </summary>
/// <returns>Elapse d time in milliseconds.</returns>
public long GetElapsed_ms()
{
lock (_backgroundThr ead)
{
StopWatch._tick CountUpdateRequ est.Set();
StopWatch._tick CountUpdateDone .WaitOne();
return (CurrentTickCou nt - StartTickCount) * 1000 /
StopWatch.Frequ ency;
}
}
}
}