Просто для полноты я хочу показать вам, как более простой код (второе решение) из принятого ответа может вернуть отрицательное значение, как отмечено в этом ответе.
В этом мысленном эксперименте используются две нити, обозначенные T1 и T2. Я префикс стековых переменных с T1 и T2, чтобы вы могли различить их (ниже).
Предположим, что lastTimeStamp начинается с 900, а текущее время - 1000.
Теперь рассмотрим следующие операции с чересстрочными нитями:
T1: long currentTimestamp = Stopwatch.GetTimestamp();
=> T1:currentTimeStamp = 1000
T2: long currentTimestamp = Stopwatch.GetTimestamp();
=> T2:currentTimeStamp = 1010
T2: var previous = Interlocked.Exchange(ref lastTimestamp, T2:currentTimestamp);
=> T2:previous = 900, lastTimestamp = 1010
T1: var previous = Interlocked.Exchange(ref lastTimestamp, T1:currentTimestamp);
=> T1:previous = 1010, lastTimestamp = 1000
T1: var ticks = (T1:currentTimestamp - T1:previous)
=> ticks = 1000 - 1010 = -10
T2: var ticks = (T2:currentTimestamp - T2:previous)
=> ticks = 1010 - 900 = 110
Как видите, поток T1 в конечном итоге вернет -10.
[Приложение]
Вот мое мнение: я не пытаюсь преобразовать метку времени секундомера в TimeSpan; Я просто оставляю это в единицах, возвращенных с Stopwatch.GetTimestamp()
для краткости (и это будет немного быстрее):
public static long Span()
{
long previous;
long current;
do
{
previous = lastTimestamp;
current = Stopwatch.GetTimestamp();
}
while (previous != Interlocked.CompareExchange(ref lastTimestamp, current, previous));
return current - previous;
}
static long lastTimestamp = Stopwatch.GetTimestamp();
Это то же решение, что и принятый выше ответ, только организовано немного по-другому.