【发布时间】:2017-04-29 15:51:15
【问题描述】:
我目前正在研究一个粒子系统,它使用一个线程,首先更新粒子,然后绘制。粒子存储在std::vector 中。我想将更新功能移至单独的线程以提高系统性能。然而,这意味着当更新线程和绘制线程同时访问std::vector 时我会遇到问题。我的更新函数将改变所有粒子的位置和颜色,并且几乎总是调整std::vector的大小。
单线程方法:
std::vector<Particle> particles;
void tick() //tick would be called from main update loop
{
//slow as must wait for update to draw
updateParticles();
drawParticles();
}
多线程:
std::vector<Particle> particles;
//quicker as no longer need to wait to draw and update
//crashes when both threads access the same data, or update resizes vector
void updateThread()
{
updateParticles();
}
void drawThread()
{
drawParticles();
}
为了解决这个问题,我使用std::mutex 进行了调查,但在实践中,由于大量粒子,线程的持续锁定意味着性能没有提高。我也调查了std::atomic,但是,粒子和std::vector 都不能简单地复制,因此也不能使用它。
使用互斥锁的多线程:
注意:我使用的是 SDL 互斥锁,据我所知,原理是一样的。
SDL_mutex mutex = SDL_CreateMutex();
SDL_cond canDraw = SDL_CreateCond();
SDL_cond canUpdate = SDL_CreateCond();
std::vector<Particle> particles;
//locking the threads leads to the same problems as before,
//now each thread must wait for the other one
void updateThread()
{
SDL_LockMutex(lock);
while(!canUpdate)
{
SDL_CondWait(canUpdate, lock);
}
updateParticles();
SDL_UnlockMutex(lock);
SDL_CondSignal(canDraw);
}
void drawThread()
{
SDL_LockMutex(lock);
while(!canDraw)
{
SDL_CondWait(canDraw, lock);
}
drawParticles();
SDL_UnlockMutex(lock);
SDL_CondSignal(canUpdate);
}
我想知道是否还有其他方法可以实现多线程方法?从本质上防止两个线程同时访问相同的数据,而不必让每个线程等待另一个。我曾考虑过制作要从中绘制的向量的本地副本,但这似乎效率低下,并且如果更新线程在复制向量时更改了向量,可能会遇到同样的问题?
【问题讨论】:
-
您还应该考虑利用数据并行性是否是更合适的方法。对于所有这些粒子,更新和绘制操作不会有那么大的不同,不是吗?
-
它们几乎是相同的功能,你能解释一下你的意思吗?我不熟悉数据并行的概念。
-
更新粒子可能发生在该向量上的某个循环中,让一个线程在前半部分工作,另一个线程在后半部分工作。泛化更多线程。
-
有趣,我喜欢这个想法,但如果一个线程调整矢量大小,它会起作用吗?这将如何影响其他线程?
-
不,那是行不通的。尽管您可以这样处理: 1. 计算所有粒子的更新函数,从而为每个粒子生成一个新/更新粒子的集合。 2. 将这些集合展平为更新的单个集合/向量。第 1 步是微不足道的并行化,第 2 步有点棘手。但是假设计算更新函数远比复制/移动粒子对象复杂得多,仍然应该得到可接受的加速。
标签: c++ multithreading c++11 opengl sdl