Секундомер не является потокобезопасным, особенно для 32-разрядных программ.
Он использует вызов Windows API QueryPerformanceCounter()
для обновления частного длинного поля. В 32-битных системах вы можете получить «рваное чтение», когда один поток читает длинное значение, а другой поток обновляет его.
Чтобы исправить это, вам нужно будет заблокировать доступ к Секундомер.
Также обратите внимание, что в некоторых старых системах были ошибки, в которых непоследовательные значения могли быть возвращены из разных потоков, вызывающих QueryPerformanceCounter()
. Из документации:
На многопроцессорном компьютере не должно иметь значения, какой процессор вызывается. Тем не менее, вы можете получить разные результаты на разных процессорах из-за ошибок в базовой системе ввода / вывода (BIOS) или слоя абстракции аппаратного обеспечения (HAL). Чтобы указать сродство процессора к потоку, используйте функцию SetThreadAffinityMask.
blockquote>Я никогда не сталкивался с этой ошибкой сам, и я не думаю, что это очень распространено.
Какие результаты вы получаете со следующей тестовой программой? Время должно быть в основном возрастающим по стоимости, но вы можете получить один или два порядка, просто потому, что их потоки будут перенесены после того, как они прочитают значение и до того, как они добавят его в очередь.
namespace Demo { class Program { Stopwatch sw = Stopwatch.StartNew(); object locker = new object(); ConcurrentQueue
queue = new ConcurrentQueue (); Barrier barrier = new Barrier(9); void run() { Console.WriteLine("Starting"); for (int i = 0; i < 8; ++i) Task.Run(()=>test()); barrier.SignalAndWait(); // Make sure all threads start "simultaneously" Thread.Sleep(2000); // Plenty of time for all the threads to finish. Console.WriteLine("Stopped"); foreach (var elapsed in queue) Console.WriteLine(elapsed); Console.ReadLine(); } void test() { barrier.SignalAndWait(); // Make sure all threads start "simultaneously". for (int i = 0; i < 10; ++i) queue.Enqueue(elapsed()); } long elapsed() { lock (locker) { return sw.ElapsedTicks; } } static void Main() { new Program().run(); } } } Сказав все это, наиболее очевидным ответом является то, что на самом деле вы не используете один секундомер между потоками, но вместо этого вы случайно запустили новый для каждого потока ...