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

秒表c#在不同的线程中表现不同?,c#,multithreading,stopwatch,C#,Multithreading,Stopwatch,我目前正在使用秒表作为全局计时器。我有一个正在运行的主线程、另一个线程和一个事件方法。 主线程启动另一个线程,事件方法由事件触发。这两种方法都将调用秒表并获取其时间。问题是,时间并不一致: 从主线程: 在9282启动REC 在19290停止记录 从另一个线程: 音频1 音频304 音频354 音频404 音频444 音频494 音频544 音频594 从事件方法: 视频4 视频5 视频29 视频61 视频97 视频129 视频161 我不明白为什么如果我在9282开始我的rec,其他两个调用秒表

我目前正在使用秒表作为全局计时器。我有一个正在运行的主线程、另一个线程和一个事件方法。 主线程启动另一个线程,事件方法由事件触发。这两种方法都将调用秒表并获取其时间。问题是,时间并不一致: 从主线程: 在9282启动REC 在19290停止记录

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

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

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

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

保存帧时,我更改为: 长a=relogio.ellapsemiliseconds 我打印出了这个值,它是正常的,正如预期的那样。但是当我打印列表中存储的值时,它们是从最开始的。奇怪吧? 很抱歉给你添麻烦了,我打印出来的时候没有起始时间,这就是为什么它们看起来都是从零开始的!非常感谢和抱歉

主线

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

    }

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

它使用Windows API调用来更新私有长字段。在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();
        }
    }
}
名称空间演示
{
班级计划
{
秒表sw=Stopwatch.StartNew();
对象锁定器=新对象();
ConcurrentQueue=新ConcurrentQueue();
屏障=新屏障(9);
无效运行()
{
控制台写入线(“启动”);
对于(int i=0;i<8;++i)
Task.Run(()=>test());
barrier.SignalAndWait();//确保所有线程“同时”启动
Thread.Sleep(2000);//所有线程都有足够的时间完成。
控制台。写入线(“停止”);
foreach(队列中经过的变量)
控制台写入线(已过);
Console.ReadLine();
}
无效测试()
{
barrier.SignalAndWait();//确保所有线程“同时”启动。
对于(int i=0;i<10;++i)
queue.Enqueue(经过的());
}
久违
{
锁(储物柜)
{
返回sw.ElapsedTicks;
}
}
静态void Main()
{
新程序().run();
}
}
}

话虽如此,最明显的答案是,事实上,您并没有在线程之间共享一个秒表,而是意外地为每个线程启动了一个新的秒表…

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

它使用Windows API调用来更新私有长字段。在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();
        }
    }
}
名称空间演示
{
班级计划
{
秒表sw=Stopwatch.StartNew();
对象锁定器=新对象();
ConcurrentQueue=新ConcurrentQueue();
屏障=新屏障(9);
无效运行()
{
控制台写入线(“启动”);
对于(int i=0;i<8;++i)
Task.Run(()=>test());
barrier.SignalAndWait();//确保所有线程“同时”启动
Thread.Sleep(2000);//所有线程都有足够的时间完成。
控制台。写入线(“停止”);
foreach(队列中经过的变量)
控制台写入线(已过);
Console.ReadLine();
}
空位