Question

I'm currently using a stopwatch as a global timer. I have main thread running, another thread, and an event method. The main thread launches the other thread and the event method is triggered by events. Both methods will call the stopwatch and get its time. The thing is, the times are not consistent: from main thread: START REC AT 9282 STOp REC AT 19290

from another thread: audio 1 audio 304 audio 354 audio 404 audio 444 audio 494 audio 544 audio 594

from event method: video 4 video 5 video 29 video 61 video 97 video 129 video 161

I don't get why if i start my rec at 9282, the other two functions that call the stopwatch will have timers that start at zero? Is this a thread related issue? How can i fix this? Thanks

UPDATE:*********

when i save my frames i changed to: long a = relogio.EllapseMilliseconds i print out this value and its ok, as expected. but when i print the value stored in the lists, they come as starting from the beggining. strange huh? SORRY FOR ALL THE TROUBLE, I PRINTED IT WITHOUT THE STARTING TIME,THATS WHY THEY ALL SEEMED TO START FROM ZERO! MANY THANKS AND SORRY!

main thread

   private void Start_Recording_Click(object sender, RoutedEventArgs e)
    {

        rec_starting_time = relogio.ElapsedMilliseconds;
        Console.WriteLine("START REC AT " + rec_starting_time);
        write_stream.enableRecording();

        Thread a = new Thread(scheduleAudioVideoFramePicks);
        a.Start();

scheduleAudioVideoFramePicks - this thread just counts the time, so i know when to stop

       //while....
      if (rec_starting_time + time_Actual > rec_starting_time+recording_time * 1000)//1000 - 1s = 1000ms
            {
                totalRecordingTimeElapsed = true;
                write_stream.disableRecording();
                Console.WriteLine("STOp REC AT " + relogio.ElapsedMilliseconds);
            }
     //end while
    lock (list_audio)
        {
        int b = 0;
        //print time of frames gathered
        foreach(AudioFrame item in list_audio){
            Console.WriteLine("audio " + (item.getTime() - rec_starting_time));
        }
        lock (list_video)
        {
        }
        foreach (VideoFrame item in list_video)
        {
             Console.WriteLine("video " + (item.getTime() - rec_starting_time));
        }
        }

the another thread, where i get the time

     if (write_stream.isRecording())
            {

                list_audio.Enqueue(new AudioFrame(relogio.ElapsedMilliseconds, audioBuffer));

            }

event method

             if (write_stream.isRecording())
                    {

                        list_video.Add(new VideoFrame(relogio.ElapsedMilliseconds, this.colorPixels));


                    }~

i dont know if this is relevant, but i start my stopwatch like this

  public MainWindow()

    {
        InitializeComponent();

        //some code

        this.relogio = new Stopwatch();
        relogio.Start();

    }
Was it helpful?

Solution

Stopwatch is not threadsafe, particularly for 32-bit programs.

It uses the Windows API call QueryPerformanceCounter() to update a private long field. On 32-bit systems you could get a "torn read" when one thread reads the long value while another thread is updating it.

To fix that, you'd have to put a lock around access to the Stopwatch.

Also note that one some older systems there were bugs where inconsistent values could be returned from different threads calling QueryPerformanceCounter(). From the documentation:

On a multiprocessor computer, it should not matter which processor is called. However, you can get different results on different processors due to bugs in the basic input/output system (BIOS) or the hardware abstraction layer (HAL). To specify processor affinity for a thread, use the SetThreadAffinityMask function.

I have never encountered this bug myself, and I don't think it's very common.

What results do you get with the following test program? The times should be mostly increasing in value, but you are liable to get one or two out of order just because their threads get rescheduled just after they've read a value and before they add it to the queue.

namespace Demo
{
    class Program
    {
        Stopwatch sw = Stopwatch.StartNew();

        object locker = new object();
        ConcurrentQueue<long> queue = new ConcurrentQueue<long>();
        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();
        }
    }
}

Having said all that, the most obvious answer is that in fact you aren't sharing a single Stopwatch between the threads, but instead you have accidentally started a new one for each thread...

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top