vir*_*a24 0 c# multithreading stopwatch
我目前正在使用秒表作为全球计时器.我有主线程运行,另一个线程和事件方法.主线程启动另一个线程,事件方法由事件触发.两种方法都会调用秒表并获得时间.问题是,时间不一致:从主线程:START REC AT 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();
Run Code Online (Sandbox Code Playgroud)
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));
}
}
Run Code Online (Sandbox Code Playgroud)
另一个线程,我有时间
if (write_stream.isRecording())
{
list_audio.Enqueue(new AudioFrame(relogio.ElapsedMilliseconds, audioBuffer));
}
Run Code Online (Sandbox Code Playgroud)
事件方法
if (write_stream.isRecording())
{
list_video.Add(new VideoFrame(relogio.ElapsedMilliseconds, this.colorPixels));
}~
Run Code Online (Sandbox Code Playgroud)
我不知道这是否相关,但我会像这样开始我的秒表
public MainWindow()
{
InitializeComponent();
//some code
this.relogio = new Stopwatch();
relogio.Start();
}
Run Code Online (Sandbox Code Playgroud)
秒表不是线程安全的,特别是对于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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
说了这么多,最明显的答案是,实际上你并没有在线程之间共享一个秒表,而是你不小心为每个线程开了一个新的秒表......