我正在C中写一个Gif动画师。
我有两个并行运行的线程。第一个允许用户改变动画的速度。第二个绘制当前帧,然后调用Sleep(Constant * 100 / CurrentSpeed)
,其中CurrentSpeed
是一个百分比金额,范围从 1 到 200 。
问题是,如果您快速将速度从 100%更改为 1%,然后再返回到第一个,则第二个线程将执行以下操作:
Sleep(Constant * 100)
这将绘制帧A,等待很多秒(尽管用户改变了速度),然后才以默认速度绘制B和后续帧。
在我看来,Sleep
在这种情况下是我的不良选择。我该怎么做才能解决这个问题?
修改
我目前拥有的代码(简体):
while (1) {
InvalidateRect(Handle, &ImageRect, FALSE);
if (shouldDispose) {
break;
}
if (DelayTime)
Sleep(DelayTime * 100 / CurrentSpeed);
SelectNextImage();
}
答案 0 :(得分:3)
不是用所需的帧速率调用Sleep()
,为什么不以1 ms的恒定间隔调用它,例如,使用变量作为计数器?
例如,让C
成为一个全局变量(计数器),它加载了1ms的“ticks”数。然后,写下循环:
while(1) { //Main loop of the player thread
if (C > 0) C--;
if (C == 0) nextframe(); //if counter reaches 0, load next frame.
Sleep(1);
}
控制线程将加载C
,其数量为1ms(即帧速率),并且播放器线程将永远不会超过1 ms。使用1ms作为基本速率是任意的。使用允许最大帧速率的最小时间,以尽可能少地加载CPU。
修改强>
经过一些热门评论(毕竟争论不错),我想指出这个解决方案是次优的,即它没有使用任何操作系统机制来发送信号线程或任何其他API来防止线程浪费CPU时间。此处显示的解决方案是通用的:它可以在任何系统中使用(即使在没有任何运行操作系统的嵌入式系统中。但最重要的是,它基于用户发布的原始代码,提出问题:使用Sleep()
我怎样才能实现自己的目的。我给他的回答很简单。无论如何,我鼓励其他人使用适当的API编写示例代码以达到同样的目标。没有难过的感觉,特别感谢Martin James。
答案 1 :(得分:1)
在您的操作系统上查找允许等待超时的同步API,例如。 Windows上的WaitForSingleObject()。如果要更改延迟,请更改超时并发出WFSO等待的事件的信号,使其“提前”返回并使用新的超时重新启动等待。
使用Sleep(1)循环进行轮询很少是合理的。
答案 2 :(得分:0)
创建一个等待计时器。当你set计时器时,你可以指定一个将在设置线程的上下文中运行的回调函数。这意味着您可以使用两个线程来完成它,但实际上只有一个线程也能正常工作。
然而,等待计时器的主要优点是它比Sleep
更准确,更可靠。计时器在概念上与Sleep
大不相同,因为Sleep
仅放弃控制,并且调度程序将线程标记为在时间到时以及调度程序运行时准备运行。它没有做任何事情。这意味着线程最终将被安排再次运行,就像任何准备就绪的其他线程一样
正在等待计时器(或其他可等待对象)的线程会导致调度程序在计时器启动时运行并暂时提升其优先级。因此,它不仅更可靠,更紧密地运行到所需的时间,而且比具有相同基本优先级的所有其他线程更早。这不提供实时保证,但至少给出了一种“软保证”。
如果您仍想使用Sleep
,请使用SleepEx
代替您可以通过queueing an APC或通过调用未记录的NtAlertThread
功能提醒您。{br / >
在任何情况下,Sleep
都很麻烦,不仅因为它不可靠,还因为它基于系统范围定时器的粒度。当然,您可以将其设置为低至1毫秒(或某些系统上的更低),但这会导致大量不必要的中断。