16

If I have a shared System.Diagnostics.Stopwatch instance, can multiple threads call shared.ElapsedTicks in a safe manner and get accurate results?

Is there any difference in terms of thread-safety/accuracy between using a shared instance of Stopwatch in this way, and using the static GetTimeStamp() method?

I'm measuring intervals of around 180ms, and finding that using the shared instance is giving me a larger spread of results, including a significant number that are shorter than I would expect.

The machine has multiple CPUs (2 * Intel X5550 for what it's worth)

Jason C
  • 38,729
  • 14
  • 126
  • 182
Rob
  • 4,327
  • 6
  • 29
  • 55
  • Just to clarify - I'm not asking whether I should share stopwatch instance members across multiple threads - that's clear from the link Magnus provided. I'm trying to understand/explain the unexpected behaviour of existing code, and wondered if a shared Stopwatch instance could be the problem. – Rob Jul 12 '11 at 14:27
  • Related possible implementation: https://stackoverflow.com/questions/37799650/simple-lockless-stopwatch – Jason C May 26 '21 at 22:25

4 Answers4

16

From MSDN:

Any instance members are not guaranteed to be thread safe.

Liam
  • 27,717
  • 28
  • 128
  • 190
Magnus
  • 45,362
  • 8
  • 80
  • 118
  • 2
    +1, your answer is right.. add to it that `Stopwatch.GetTimestamp();` is thread safe because it is static. – Jalal Said Jul 12 '11 at 13:24
  • 1
    There used to be thread-safety information in MSDN documentation. But has apparently been removed. Also code review and empirical testing seems to suggest that ElapsedXXX properties are thread safe as suggested by more recent answers. – blaz Apr 15 '20 at 09:36
  • @JalalSaid while that method should be threadsafe, would it give accurate results if you first call it on one thread and then a diffrent one and then subtract them to get the elapsed time? – Peter Dec 26 '21 at 16:53
10

Looking at the source code, it is thread-safe, but you must not use: Stop(), Reset() and Restart().

So, if you start a shared instance, does not modify it and call only ElapsedXXX properties, you should be fine.

xmedeko
  • 7,336
  • 6
  • 55
  • 85
  • 1
    @apdevelop No, `ElapsedXXX` are not atomic, that's why you must not use `Stop(), Reset(), Restart()` from other thread. Just look at the source code. – xmedeko Oct 17 '19 at 18:11
  • I meant creating `Stopwatch` and calling its `Start`/`Stop` methods in one thread and then calling `ElapsedXXX` methods from another thread(s). _Atomic_ reading related to dealing with `Int64` fields from different threads, when we should use methods from `Interlocked` class. In case of reading `ElapsedMilliseconds` property it's not possible. – apdevelop Oct 18 '19 at 11:07
  • 2
    Empirical test on 64-bit machine with .NET 4.8 seems to confirm thread-safety of ElapsedXXX properties. – blaz Apr 15 '20 at 09:33
  • 3
    While this property is theadsafe as long as you do not call the `Start()`, `Reset()`, or `Stop()` methods, it is important to note that if you are using the ticks for arithmetic and then passing the value into a `TimeSpan` constructor, for example, one `stopWatch.ElapsedTicks` Tick is not equivalent to `TimeSpan` Ticks as described here: http://geekswithblogs.net/BlackRabbitCoder/archive/2012/01/12/c.net-little-pitfalls-stopwatch-ticks-are-not-timespan-ticks.aspx. To get equivalent ticks you would want to use `stopWatch.Elapsed.Ticks`. – Robin Zimmerman Jun 30 '20 at 16:24
5

Looking at the source code, it is not thread-safe.

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • Are you able to infer what inaccuracies might occur? I ask because although I'm seeing unexpected results, they are not "impossible" ones - I'm not getting any negative measurements, or results that are orders of magnitude out. Even after millions of measurements. – Rob Jul 12 '11 at 13:29
  • 1
    On 32-bit machines, it's vulnerable to tearing if called while calling `Start()` or `Stop()`. (it might read the `long elapsed` field while it's being written to). On x64, it looks safe. – SLaks Jul 12 '11 at 13:38
  • 1
    Does the `ElapsedTicks` method modify the underlying instance in any fashion that would cause multiple threads using that method to interfere with each other, if no such calls are simultaneous with other methods like `Start`, `Stop`, etc.? I wouldn't think `ElapsedTicks` would need to modify any instance members at all. – supercat Aug 11 '15 at 20:17
4

You can use https://msdn.microsoft.com/en-us/library/dd642243(v=vs.110).aspx

ThreadLocal<T> 

like this:

    ThreadLocal<Random> _localRandom = new ThreadLocal<Random>(() => new Random());
    ThreadLocal<Stopwatch> _localStopwatch = new ThreadLocal<Stopwatch>(() => new Stopwatch());

    public void SomeTest()
    {
        Action someAction = () =>
            {
                _localStopwatch.Value.Reset();
                _localStopwatch.Value.Start();
                Thread.Sleep(_localRandom.Value.Next(100, 500));
                _localStopwatch.Value.Stop();
                Debug.Print(_localStopwatch.Value.Elapsed.TotalMilliseconds.ToString(CultureInfo.InvariantCulture));
            };

        var actions = Enumerable.Range(0, 1000).Select(i => someAction).ToArray();
        Parallel.Invoke(new ParallelOptions {MaxDegreeOfParallelism = Environment.ProcessorCount}, actions);
    }
Denis
  • 833
  • 3
  • 12
  • 22
  • 1
    This answer is not about Stopwatch thread-safety. It is about how to provide separate Stopwatch instance to each thread. – blaz Apr 15 '20 at 09:32
  • 1
    This seems awfully convoluted, why use `ThreadLocal` when you can just declare a local variable inside the thread? – Liam Aug 07 '20 at 08:13
  • @Liam, yes, you can use the ThreadStatic attribute, but keep in mind that the default initializer will only call once for all threads, not one for each thread. So, I prefer to use ThreadLocal everywhere – Denis Aug 18 '20 at 09:18