![](/img/trans.png)
[英]C++, how to make efficient, 1 shared array of resources for multiple threads
[英]Multiple threads access shared resources
我目前正在研究一個粒子系統,該系統使用一個線程,首先對粒子進行更新,然后繪制。 粒子存儲在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);
}
我想知道是否還有其他方法可以實現多線程方法? 本質上是防止兩個線程同時訪問相同的數據,而不必讓每個線程都等待另一個線程。 我曾考慮過制作要從中繪制的矢量的本地副本,但這似乎效率不高,並且如果更新線程在復制矢量時更改了矢量,可能會遇到相同的問題?
我將使用更精細的鎖定策略。 與其在vector
中存儲particle
對象,不如存儲指向其他對象的指針。
struct lockedParticle { particle* containedParticle; SDL_mutex lockingObject; };
在updateParticles()
我將嘗試使用SDL_TryLockMutex()
獲取單個鎖定對象-如果無法獲得對互斥鎖的控制,我會將指向該特定lockedParticle
實例的指針添加到另一個向量,然后稍后重試以更新它們。
我將在drawParticles()
遵循類似的策略。 這依賴於這樣的事實,即繪制順序對於粒子並不重要,通常是這種情況。
如果不關心數據一致性,則可以通過將vector封裝在自定義類中並僅在單個讀/寫操作上設置互斥鎖來避免阻塞整個vector,例如:
struct SharedVector
{
// ...
std::vector<Particle> vec;
void push( const& Particle particle )
{
SDL_LockMutex(lock);
vec.push_back(particle);
SDL_UnlockMutex(lock);
}
}
//...
SharedVector particles;
然后,當然,您需要修改updateParticles()
和drawParticles()
以使用新類型,而不是std::vector
。
編輯:您可以避免通過在updateParticles()
和drawParticles()
方法中使用互斥對象來創建新結構,例如
void updateParticles()
{
//... get Particle particle object
SDL_LockMutex(lock);
particles.push_back(particle);
SDL_UnlockMutex(lock);
}
對於drawParticles()
也應執行相同的操作。
如果向量一直在變化,則可以使用兩個向量。 drawParticles
將具有其自己的副本,而updateParticles
將寫入另一個副本。 完成這兩個函數后,將updateParticles
使用的向量交換,復制或移動到drawParticles
要使用的drawParticles
。 ( updateParticles
可以從drawParticles
使用的相同向量中讀取,以獲取當前粒子的位置,因此您無需創建一個完整的新副本。)無需鎖定。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.