秒表c#在不同的线程中表现不同?

时间:2013-07-06 18:21:22

标签: c# multithreading stopwatch

我目前正在使用秒表作为全球计时器。我有主线程运行,另一个线程和事件方法。 主线程启动另一个线程,事件方法由事件触发。两种方法都会调用秒表并获得时间。问题是,时代不一致: 来自主线程: 开始录制9282 STOp REC AT 19290

来自另一个主题: 音频1 音频304 音频354 音频404 音频444 音频494 音频544 音频594

来自事件方法的

: 视频4 视频5 视频29 视频61 视频97 视频129 视频161

我不明白为什么如果我在9282开始我的rec,另外两个调用秒表的功能将有从零开始的计时器?这是线程相关的问题吗?我怎样才能解决这个问题?感谢

更新: * ** * ** * **

当我保存我的帧时,我改为: long a = relogio.EllapseMilliseconds 我正如预期的那样打印出这个值和它的确定。但是当我打印存储在列表中的值时,它们从开始时开始。奇怪吧? 对于所有的麻烦感到厌烦,我在没有开始的时候打印它,为什么他们都试图从零开始!非常感谢和抱歉!

主线程

   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 - 此线程只计算时间,所以我知道何时停止

       //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));
        }
        }

另一个线程,我得到时间

     if (write_stream.isRecording())
            {

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

            }

事件方法

             if (write_stream.isRecording())
                    {

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


                    }~

我不知道这是否相关,但我会像这样开始我的秒表

  public MainWindow()

    {
        InitializeComponent();

        //some code

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

    }

1 个答案:

答案 0 :(得分:5)

秒表不是线程安全的,特别是对于32位程序。

它使用Windows API调用QueryPerformanceCounter()来更新私有长字段。在32位系统上,当一个线程读取long值而另一个线程正在更新它时,你可能会得到“撕裂读取”。

要解决这个问题,你必须锁定对秒表的访问。

另请注意,有些旧系统存在错误,可能会从调用QueryPerformanceCounter()的不同线程返回不一致的值。来自文档:

  

在多处理器计算机上,调用哪个处理器无关紧要。但是,由于基本输入/输出系统(BIOS)或硬件抽象层(HAL)中的错误,您可以在不同的处理器上获得不同的结果。要指定线程的处理器关联,请使用SetThreadAffinityMask函数。

我自己从未遇到过这个错误,我认为这种情况并不常见。

您通过以下测试计划获得了哪些结果?时间应该大部分都在增加,但是你可能会因为他们的线程在读取值之后以及在将它们添加到队列之前重新安排而导致一个或两个乱序。

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();
        }
    }
}

说了这么多,最明显的答案是,实际上你并没有在线程之间共享一个秒表,而是你不小心为每个线程开了一个新的...... [/ p>