0

我目前正在使用秒表作为全局计时器。我有主线程正在运行,另一个线程和一个事件方法。主线程启动另一个线程,事件方法由事件触发。这两种方法都会调用秒表并获取它的时间。问题是,时间不一致:来自主线程:START REC AT 9282 STOP REC AT 19290

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

来自事件方法:视频4 视频5 视频29 视频61 视频97 视频129 视频161

我不明白为什么如果我在 9282 开始我的记录,那么调用秒表的其他两个函数的计时器将从零开始?这是与线程相关的问题吗?我怎样才能解决这个问题?谢谢

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

当我保存我的帧时,我更改为: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();

    }
4

1 回答 1

5

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

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

要解决此问题,您必须锁定对秒表的访问权限。

另请注意,一些较旧的系统存在错误,其中可能从不同的线程调用返回不一致的值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();
        }
    }
}

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

于 2013-07-06T18:40:32.797 回答