我正在创建一组需要在帧周期中工作的线程。我想控制在一秒钟内完成了多少帧。我将我的代码简化为这个,这样我就可以向您展示我所写的内容。
// setup the frame timer
std::chrono::time_point<std::chrono::system_clock> start = std::chrono::system_clock::now();
std::chrono::time_point<std::chrono::system_clock> end = std::chrono::system_clock::now();
while(running == true)
{
// update timer
start = std::chrono::system_clock::now();
std::chrono::duration<double> elapsed_seconds = start - end;
double frameTime = elapsed_seconds.count();
this->Work(frameTime);
// update timer
std::chrono::time_point<std::chrono::system_clock> afterWork = std::chrono::system_clock::now();
std::chrono::duration<double> elapsedWorkTime = afterWork - end ;
const double minWorkTime = 1000 / this->timer.NumberOfFramePerSeconds;
if(elapsedWorkTime.count() < minWorkTime)
{
double timeToSleep = minWorkTime - elapsedWorkTime.count();
std::this_thread::sleep_for(std::chrono::milliseconds((int)timeToSleep));
}
// update fps
end = start;
timer.FrameCount += 1;
}并不是所有线程都有相同的工作量,有些线程比其他线程有更多的工作,因此如果没有睡眠,我就会得到围绕这个Thread 1 : 150fps, Thread 2: 5000fps, Thread 3: 5000fps, Thread 4: 5000fps的结果。
我想要的是能够将帧每秒设置为60,所以使用上面的代码,我会将this->timer.NumberOfFramePerSeconds设置为60。我的问题是,当我这样做的时候,我得到了一个像Thread 1 : 30fps, Thread 2: 60fps, Thread 3: 60fps, Thread 4: 60fps这样的结果
这表明我的代码有问题,因为当我注释掉睡眠时,线程1将很高兴地完成超过150帧,但是当睡眠在那里时,它将工作在我试图实现的60以下。
我的代码/算法正确吗?
发布于 2014-08-04 15:16:01
在我看来,您可能有一个单位转换错误。这通常是由于在chrono类型系统之外处理时间单位和引入显式转换因素造成的。例如:
double frameTime = elapsed_seconds.count();
this->Work(frameTime);Work可以修改为采用duration<double>而不是double吗?这将有助于确保double不会被重新解释为Work内部的秒以外的其他内容。如果由于某些原因不能,则至少可以减少以下错误的暴露:
this->Work(elapsed_seconds.count());以下情况看上去很可疑:
std::chrono::duration<double> elapsedWorkTime = afterWork - end ;
const double minWorkTime = 1000 / this->timer.NumberOfFramePerSeconds;
if(elapsedWorkTime.count() < minWorkTime)elapsedWorkTime显然有秒的单位。minWorkTime似乎有毫秒的单位。if扔掉所有单位的信息。这看起来应该是:
std::chrono::duration<double> minWorkTime(1./this->timer.NumberOfFramePerSeconds);
if(elapsedWorkTime < minWorkTime)或者如果您真的希望minWorkTime表示毫秒(这不是必要的):
std::chrono::duration<double, std::milli> minWorkTime(1000./this->timer.NumberOfFramePerSeconds);
if(elapsedWorkTime < minWorkTime)我推荐前者,因为1000的引入只是错误出现的另一个机会。
double timeToSleep = minWorkTime - elapsedWorkTime.count();
std::this_thread::sleep_for(std::chrono::milliseconds((int)timeToSleep));在这里,您将不必要地离开chrono的安全网,手动执行操作。相反,这应该是:
std::this_thread::sleep_for(minWorkTime - elapsedWorkTime);或者如果你想变得更详细:
auto timeToSleep = minWorkTime - elapsedWorkTime;
std::this_thread::sleep_for(timeToSleep);.count()的使用应该是罕见的。目前在打印duration时需要使用它,也可能需要处理无法使用chrono的遗留代码。如果您发现自己使用的.count()更多,请后退一步,尝试删除它。chrono有助于防止单元转换错误,当您通过.count()退出系统时,会破坏chrono存在的原因。
更新
VS2013有一个错误,它不会在双基式持续时间上睡觉。所以作为解决办法..。
std::this_thread::sleep_for(std::chrono::duration_cast
<std::chrono::milliseconds>(minWorkTime - elapsedWorkTime));这很难看,但仍然把所有的转换因素都留给chrono。
感谢Caesar为我测试了这个解决方案。
发布于 2014-08-04 14:13:03
简单的答案是你没有。参见this SO question。
与其控制易出错且不能很好地处理慢速线程的framerate,您应该尽可能地设置代码来处理当前的框架。
让线程做他们合理能做的工作。如果由于某种原因不得不暂停,请使用消息队列,并在消息队列为空时休眠。
发布于 2014-08-04 16:53:56
睡眠通常不是很精确,因为时钟的分辨率可能很低。
例如,在Windows上,典型的定时器分辨率大约为15.6ms。如果你只要求睡眠1毫秒,它就会睡到下一个15.6毫秒的间隔,在未来至少是1毫秒。平均而言,这比你预期的要长6.8毫秒。一旦时间流逝,您就无法保证处理器会立即安排您的线程。
因此,我怀疑您的线程需要睡眠一段时间,但它的睡眠时间要长得多,并且错过了下一个框架。(其他线程的运行速度正好是60 fps,这意味着您还拥有其他的速率限制器,比如垂直同步。)
一个可能的解决方案,尽管效率低下,是做一个繁忙-等待,而不是睡眠的任何时间比时钟分辨率短。例如,如果您必须延迟3.2*clock_resolution,那么您可以为3*clock_resolutions睡觉,然后在循环中旋转,直到实际时间到来。
另一种解决方案是让线程等待一个同步原语,该原语以所需的速率发出信号。根据可用的原语类型,当您试图同步多个线程时,这可能会很棘手。
https://stackoverflow.com/questions/25120825
复制相似问题