- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在编写一个小游戏,我想将我的渲染器与主循环分离。在主循环中,我想更新我的输入,我不想等到我的渲染器完成绘制,但这意味着我只想在渲染器完成绘制时发出绘制命令。
我需要一种方法来了解旧的渲染作业是否已完成,以便我可以开始新的渲染作业。
#include <asio.hpp>
#include <memory>
#include <thread>
#include <iostream>
#include <mutex>
#include <chrono>
struct ready
{
bool is_ready;
std::mutex m;
void set(bool b)
{
std::lock_guard<std::mutex> g(m);
is_ready = b;
}
operator bool()
{
std::lock_guard<std::mutex> g(m);
return is_ready;
}
ready()
: is_ready(true)
{
}
};
int
main()
{
auto service = std::make_shared<asio::io_service>();
auto w = std::make_shared<asio::io_service::work>(*service);
std::thread t1([&] { service->run(); });
std::thread t2([&] { service->run(); });
auto ready_sp = std::make_shared<ready>();
while (ready_sp) {
if (*ready_sp) {
ready_sp->set(false);
service->dispatch([ready_sp] {
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "Draw on thread: " << std::this_thread::get_id()
<< std::endl;
ready_sp->set(true);
});
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
std::cout << "Doing other stuff" << std::endl;
}
w.reset();
t1.join();
t2.join();
}
这就是我的大致做法吗?
最佳答案
好的,下面是我可能如何处理它的示例。
此代码有一个线程用于渲染(但我们可以使用更多线程)并使用主线程来玩游戏。
我已将代码拆分为多个关注点:
scene
是描述场景状态的数据
render
是渲染场景的免费函数。它不知道线程、锁、互斥锁或内存管理。
renderer
是一个在其自己的线程中渲染场景的对象,但仅在被告知时才这样做。
scene_update
封装了对场景进行大量增量更新的概念,但渲染器应该只在所有增量完成后才被要求渲染 - 即原子更新。
本例模拟场景更新计算耗时300ms,实际渲染耗时1s。因此,我们应该看到每 ~3 次更新 1 次渲染。
希望您会同意,由于整个程序使用值语义 并封装了所有内存管理和线程问题,所以程序的主体非常易于阅读。
主要是:
int main()
{
using namespace std;
// create my scene
scene my_scene;
// instantiate my renderer
renderer my_renderer;
// tell the renderer that the scene may be rendered
my_renderer.notify(my_scene);
// ... while it is doing that...
// ... lets make our hero march across the wilderness
for (int x = 0 ; x < 10 ; ++x)
{
for(int y = 0 ; y < 10 ; ++y)
{
// perform a scene update. the calculations for this update
// take 300ms (faster than the renderer)
scene_update u(my_scene, my_renderer);
{
my_scene.data().hero_x = x;
my_scene.data().hero_y = y;
this_thread::sleep_for(chrono::milliseconds(300));
}
// tell the renderer that there is a new scene to render
u.commit();
}
}
return 0;
}
同样render
也很简单:
void render(const scene& s)
{
using namespace std;
const auto& data = s.data();
cout << "the hero is at ";
cout.flush();
this_thread::sleep_for(chrono::milliseconds(500));
cout << data.hero_x << ", ";
cout.flush();
this_thread::sleep_for(chrono::milliseconds(500));
cout << data.hero_y << '\n';
cout.flush();
}
完整程序如下:
#include <iostream>
#include <vector>
#include <string>
#include <condition_variable>
#include <thread>
#include <memory>
#include <cassert>
//
// a simple scene object with *value semantics*
// the actual scene data is stored in an inner class, an instance of which is maintained by a unique_ptr
// we could have used a shared_ptr but there is no reason to since we will be taking copies of the scene
// data in order to render it out of line.
// doing it this way means that although the copy might be expensive, it is only performed once
// moves are extremely fast
struct scene
{
// a type to allow us to create an unitialised scene explicitly
struct none_type {};
// a flag object
static constexpr const none_type none = none_type();
// this is the actual expensive scene data (simulated)
struct expensive_large_scene_data
{
int hero_x = 0,
hero_y = 0;
};
// a printer function (to help debugging)
friend std::ostream& operator<<(std::ostream& os, const expensive_large_scene_data& s)
{
os << "(" << s.hero_x << ", " << s.hero_y << ")";
return os;
}
// construct empty
scene(none_type) {
// no not initialise the pointer
}
// construct and initialise a default scene
scene() : _data(std::make_unique<expensive_large_scene_data>()) {}
// copy constructor must explicitly clone the pointer (if populated)
scene(const scene& r)
: _data(r
? std::make_unique<expensive_large_scene_data>(r.data())
: nullptr)
{}
// move constructor
scene(scene&& r)
: _data(std::move(r._data))
{}
// copy-assignment - take care here too.
scene& operator=(const scene& r)
{
_data = r
? std::make_unique<expensive_large_scene_data>(r.data())
: nullptr;
return *this;
}
// move-assignment is simple
scene& operator=(scene&& r)
{
_data = std::move(r._data);
return *this;
}
// no need for a destructor - we're using unique_ptr
bool valid() const {
return bool(_data.get());
}
// convertible to bool so we can check whether it is empty easily
operator bool() const {
return valid();
}
void reset() {
_data.reset();
}
// accessor
const expensive_large_scene_data& data() const {
assert(_data.get());
return *_data;
}
expensive_large_scene_data& data() {
assert(_data.get());
return *_data;
}
private:
std::unique_ptr<expensive_large_scene_data> _data;
};
std::ostream& operator<<(std::ostream& os, const scene& s)
{
return os << s.data();
}
// a function that renders a scene
// this one takes a second to complete
void render(const scene& s)
{
using namespace std;
const auto& data = s.data();
cout << "the hero is at ";
cout.flush();
this_thread::sleep_for(chrono::milliseconds(500));
cout << data.hero_x << ", ";
cout.flush();
this_thread::sleep_for(chrono::milliseconds(500));
cout << data.hero_y << '\n';
cout.flush();
}
// the renderer
struct renderer
{
using mutex_type = std::mutex;
using lock_type = std::unique_lock<mutex_type>;
// start thread in constructor - do not copy this object (you can't anyway because of the mutex)
renderer()
: _render_thread(std::bind(&renderer::loop, this))
{}
// shut down cleanly on destruction
~renderer()
{
auto lock = lock_type(_mutex);
_cancelled = true;
lock.unlock();
if (_render_thread.joinable())
{
_render_thread.join();
}
}
// notify the renderer that a new scene is ready
void notify(const scene& s)
{
auto lock = lock_type(_mutex);
_pending_scene = s;
lock.unlock();
_cv.notify_all();
}
private:
void loop()
{
for(;;)
{
auto lock = lock_type(_mutex);
_cv.wait(lock, [this] {
// wait for either a cancel event or for a new scene to be ready
return _cancelled or _pending_scene;
});
if (_cancelled) return;
// move the pending scene to our scene-render buffer - this is very cheap
_current_scene = std::move(_pending_scene);
_pending_scene.reset();
lock.unlock();
// unlock early to allow mainline code to continue
// now take our time rendering the scene
render(_current_scene);
_current_scene.reset();
}
}
private:
mutex_type _mutex;
std::condition_variable _cv;
bool _cancelled = false;
scene _pending_scene = scene(scene::none);
scene _current_scene = scene(scene::none);
std::thread _render_thread;
};
// an object to connect a scene update 'transaction' with the renderer
struct scene_update
{
scene_update(scene& s, renderer& r)
: _s(s), _r(r) {}
void commit()
{
_r.notify(_s);
}
scene& _s;
renderer& _r;
};
int main()
{
using namespace std;
// create my scene
scene my_scene;
// instantiate my renderer
renderer my_renderer;
// tell the renderer that the scene may be rendered
my_renderer.notify(my_scene);
// ... while it is doing that...
for (int x = 0 ; x < 10 ; ++x)
{
for(int y = 0 ; y < 10 ; ++y)
{
// perform a scene update. the calculations for this update
// take 300ms (faster than the renderer)
scene_update u(my_scene, my_renderer);
{
my_scene.data().hero_x = x;
my_scene.data().hero_y = y;
this_thread::sleep_for(chrono::milliseconds(300));
}
// tell the renderer that there is a new scene to render
u.commit();
}
}
return 0;
}
预期输出:
the hero is at 0, 0
the hero is at 0, 2 <<-- note the missing updates
the hero is at 0, 5 <<-- because rendering takes longer
the hero is at 0, 8 <<-- than calculation
the hero is at 1, 2
关于c++ - 如果旧工作已经完成,如何开始新工作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34434733/
从 Redis 获取消息时,onDone:(){print('done')} 从未起作用。 import 'package:dartis/dartis.dart' as redis show PubS
昨天我玩了一些vim脚本,并设法通过循环来对当前输入的内容进行状态栏预测(请参见屏幕截图(灰色+黄色栏))。 问题是,我不记得我是怎么得到的,也找不到我用于该vim魔术的代码片段(我记得它很简单):它
我尝试加载 bash_completion在我的 bash (3.2.25) 中,它不起作用。没有消息等。我在我的 .bashrc 中使用了以下内容 if [ -f ~/.bash_completio
我正在尝试构建一个 bash 完成例程,它将建议命令行标志和合适的标志值。例如在下面 fstcompose 命令我想比赛套路先建议 compose_filter= 标志,然后建议来自 [alt_seq
当我尝试在重定向符号后完成路径时,bash 完成的行为就好像它仍在尝试在重定向之前完成命令的参数一样。 例如: dpkg -l > /med标签 通过在 /med 之后点击 Tab我希望它完成通往 /
我的类中有几个 CAKeyframeAnimation 对象。 他们都以 self 为代表。 在我的animationDidStop函数中,我如何知道调用来自哪里? 是否有任何变量可以传递给 CAKe
我有一个带有 NSDateFormatter 的 NSTextField。格式化程序接受“mm/dd/yy”。 可以自动补全日期吗?因此,用户可以输入“mm”,格式化程序将完成当前月份和年份。 最佳答
有一个解决方案可以使用以下方法完成 NSTextField : - (NSArray *)control:(NSControl *)control textView:(NSTextView *)tex
我正在阅读 Passport 的文档,我注意到 serialize()和 deserialize() done()被调用而不被返回。 但是,当使用 passport.use() 设置新策略时在回调函数
在 ubuntu 11.10 上的 Firefox 8.0 中,尽管 img.complete 为 false,但仍会调用 onload 函数 draw。我设法用 setTimeout hack 解决
假设我有两个与两个并行执行的计算相对应的 future 。我如何等到第一个 future 准备好?理想情况下,我正在寻找类似于Python asyncio's wait且参数为return_when=
我正在寻找一种 Java 7 数据结构,其行为类似于 java.util.Queue,并且还具有“最终项目已被删除”的概念。 例如,应可以表达如下概念: while(!endingQueue.isFi
这是一个简单的问题。 if ($('.dataTablePageList')) { 我想做的是执行一个 if 语句,该语句表示如果具有 dataTablesPageList 类的对象也具有 menu
我用replaceWith批量替换了许多div中的html。替换后,我使用 jTruncate 来截断文本。然而它不起作用,因为在执行时,replaceWith 还没有完成。 我尝试了回调技巧 ( H
有没有办法调用 javascript 表单 submit() 函数或 JQuery $.submit() 函数并确保它完成提交过程?具体来说,在一个表单中,我试图在一个 IFrame 中提交一个表单。
我有以下方法: function animatePortfolio(fadeElement) { fadeElement.children('article').each(function(i
我刚刚开始使用 AndEngine, 我正在像这样移动 Sprite : if(pValueY < 0 && !jumping) { jumping =
我正在使用 asynctask 来执行冗长的操作,例如数据库读取。我想开始一个新 Activity 并在所有异步任务完成后呈现其内容。实现这一目标的最佳方法是什么? 我知道 onPostExecute
我有一个脚本需要命令名称和该命令的参数作为参数。 所以我想编写一个完成函数来完成命令的名称并完成该命令的参数。 所以我可以这样完成命令的名称 if [[ "$COMP_CWORD" == 1 ]];
我的应用程序有一个相当奇怪的行为。我在 BOOT_COMPLETE 之后启动我的应用程序,因此在我启动设备后它是可见的。 GUI 响应迅速,一切正常,直到我调用 finish(),按下按钮时,什么都没
我是一名优秀的程序员,十分优秀!