簡體   English   中英

多線程訪問共享資源

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM