2016-02-05 68 views
3

我有一个线程安全的对象队列,它被设计用来模拟在一系列线程之间移动的工作流水线。在某些情况下,我想传递非线程安全的对象(例如,std::vector或其他STL容器)作为这些工作项目的一部分。通过线程安全的容器传递非线程安全的对象

现在,在情况下,你有线程之间的共享对象,有加载/存储排序在确保对象的一致性一个明显的问题。由于线程安全队列确保只有一个线程拥有对象的所有权,因此多个线程不可能同时尝试修改或读取对象..我所看到的唯一可能的问题是确保先前发出的负载中的内存一致性/由先前的所有者线程存储在对象上。

该队列通过在队列操作上创建lock_guard<...>来确保线程安全。由于内存屏蔽和同步将由lock_guard负责保护,线程之间移动的对象的内存一致性是否会得到保证?我的

部分希望确保我只是在线程之间传递线程安全的对象,但我觉得这种情况下,应该没有问题。这是真的?

+1

听起来像是程序员的一个很好的问题.SE,FWIW。 –

+0

@PreferenceBean我认为这是一个很好和有效的问题(不过SE程序员也没有这样的经验)。 –

+0

@πάνταῥεῖ:它没有代码,我希望看到我们向程序员发送更多优质内容,所以我们不会饿死它。但我不反对。 –

回答

5

该队列通过在队列操作中创建lock_guard < ...>来确保线程安全。由于lock_guard会保护内存屏蔽和同步,因此不会保证在线程之间移动的对象的内存一致性?

是的。

我的一部分想确保我只是在线程之间传递线程安全的对象,但我觉得这种情况应该没有问题。这是真的?

是的。

这基本上为什么volatile不适用于在C++中同时数据存取的装置;它并不能解决竞争条件,并且一旦你将并发设备(例如互斥锁)带入战场来解决这个问题,那些也会考虑到内存一致性问题,所以volatile没有什么可做的了。

+0

我想添加一条关于这个的评论:因为我仍然在学习多线程,如果您正在使用Windows平台,请在MSDN上查找CRITICAL_SECTION以及它是如何工作的。 –

+1

@FrancisCugler没有'CRITICAL_SECTION'就在这里。 OP明确询问了标准C++'lock_guard's。 –

+0

感谢您的快速回复。我仍然努力确保一致性,同时最大限度地提高C++ 11内存模型的性能。在无锁编程中,我假定只要轻松的排序可以通过原子RMW避免,情况也是一样的。 – 0n1

0

我使用略有不同的方法来阻塞线程,我不知道这是否会以任何方式或不帮助,但我不介意分享我所提供的不同意见或办法。

BlockThread.h

#ifndef BLOCK_THREAD_H 
#define BLOCK_THREAD_H 

namespace vmk { 

class BlockThread sealed { 
private: 
    CRITICAL_SECTION* m_pCriticalSection; 

public: 
    explicit BlockThread(CRITICAL_SECTION& criticalSection); 
    ~BlockThread(); 

private: 
    BlockThread(const BlockThread& c); // Not Implemented 
    BlockThread& operator=(const BlockThread& c); // Not Implemented 
    // void swap(BlockThread& other) throw(); 

}; // BlockThread 

} // namespace vmk 

#endif // BLOCK_THREAD_H 

BlockThread.cpp

#include "stdafx.h" 
#include "BlockThread.h" 

namespace vmk { 

// ---------------------------------------------------------------------------- 
// BlockThread() 
BlockThread::BlockThread(CRITICAL_SECTION& criticalSection) { 
    m_pCriticalSection = &criticalSection; 
    EnterCriticalSection(m_pCriticalSection); 
} // BlockThread 

// ---------------------------------------------------------------------------- 
// ~BlockThread() 
BlockThread::~BlockThread() { 
    LeaveCriticalSection(m_pCriticalSection); 
} // ~BlockThread  

} // namespace vmk 

VolatileLocker.h

#ifndef VOLATILE_LOCKER_H 
#define VOLATILE_LOCKER_H 

namespace vmk { 

template <typename T> 
class VolatileLocker { 
private: 
    T*     m_pObject; 
    CRITICAL_SECTION* m_pCriticalSection; 

public: 
    VolatileLocker(volatile T& objectToLock, CRITICAL_SECTION& criticalSection); 
    ~VolatileLocker(); 

    T* operator->(); 

private: 
    VolatileLocker(const VolatileLocker& c); // Not Implemented 
    VolatileLocker& operator=(const VolatileLocker& c); // Not Implemented 

}; // VolatileLocker 

#include "VolatileLocker.inl" 

} // namespace vmk 

#endif // VOLATILE_LOCKER_H 

// reference: http://drdobbs.com/cpp/184403766 

VolatileLocker。INL

// ---------------------------------------------------------------------------- 
// VolatileLocker() 
// Locks A Volatile Variable So That It Can Be Used Across Multiple Threads Safely 
template<typename T> 
VolatileLocker<T>::VolatileLocker(volatile T& objectToLock, CRITICAL_SECTION& criticalSection) : 
m_pObject(const_cast<T*>(&objectToLock)), 
m_pCriticalSection(&criticalSection) { 
    EnterCriticalSection(m_pCriticalSection); 
} // VolatileLocker 

// ---------------------------------------------------------------------------- 
// ~VolatileLocker() 
template<typename T> 
VolatileLocker<T>::~VolatileLocker() { 
    LeaveCriticalSection(m_pCriticalSection); 
} // ~VolatileLocker 

// ---------------------------------------------------------------------------- 
// operator->() 
// Allow The Locked Object To Be Used Like A Pointer 
template <typename T> 
T* VolatileLocker<T>::operator->() { 
    return m_pObject; 
} // operator-> 

下面是一个使用BlockThread类对象 - 这个类取决于这里没有显示其他类和我将只包括本类使用BlockThread对象的部分。

AudioManager.cpp

#include "stdafx.h" 
#include "AudioManager.h" 

#include "AudioBuffer.h" 
#include "AudioSource.h" 
#include "BlockThread.h" 
#include "Logger.h" 

namespace vmk { 

static AudioManager*  s_pAudioManager = nullptr; 
static CRITICAL_SECTION  s_csChangeSources; 

// ---------------------------------------------------------------------------- 
// AudioManager() 
AudioManager::AudioManager() : 
Singleton(Singleton::TYPE_AUDIO_MANAGER) { 
    InitializeCriticalSection(&s_csChangeSources); 

    if (!alutInit(NULL, NULL)) { 
     std::ostringstream strStream; 
     strStream << __FUNCTION__ << " ALUT error: " << alutGetErrorString(alutGetError()); 
     throw ExceptionHandler(strStream); 
    } 

    alGetError(); // Clear Errors 

    alListenerf(AL_GAIN, 1.0f); // Master Volume 
    alListener3f(AL_POSITION, 0.0f, 0.0f, 0.0f); 
    alListener3f(AL_VELOCITY, 0.0f, 0.0f, 0.0f); 

    float f6Orient[] = { 0.0f, 0.0f, -1.0f, // Forward(X, Y, Z) 
         0.0f, 1.0f, 0.0f }; // Up(X,Y,Z) 

    alListenerfv(AL_ORIENTATION, f6Orient); 
    if (alGetError() != AL_NO_ERROR) { 
     std::ostringstream strStream; 
     strStream << __FUNCTION__ << " Failed to initialize Listener"; 
     throw ExceptionHandler(strStream); 
    } 

    s_pAudioManager = this; 
} // AudioManager 

// ---------------------------------------------------------------------------- 
// ~AudioManager() 
AudioManager::~AudioManager() { 
    s_pAudioManager = nullptr; 

    m_mAudioSources.clear(); 
    m_lAudioBuffers.clear(); 

    alutExit(); 

    DeleteCriticalSection(&s_csChangeSources); 
} // ~AudioManager 

// ---------------------------------------------------------------------------- 
// get() 
AudioManager* const AudioManager::get() { 
    if (nullptr == s_pAudioManager) { 
     throw ExceptionHandler(__FUNCTION__ + std::string(" failed, AudioManager has not been constructed yet")); 
    } 
    return s_pAudioManager; 
} // get  

// ---------------------------------------------------------------------------- 
// Create A Sound Source Using The Passed In Filename. If The File Is Not 
// Already Loaded Into A Memory Buffer, Then A New Buffer Is Created. 
void AudioManager::createSource(SoundSource eSoundSource, const std::string& strFilename, bool bAttachToListener) { 
    BlockThread blockThread(s_csChangeSources); 

    if (!isAvailable(eSoundSource)) { 
     return; 
    } 

    std::shared_ptr<AudioBuffer> pAudioBuffer = nullptr; 

    // Check If This File Has Already Been Loaded Into A Buffer 
    for (ListAudioBuffers::iterator itBuffer = m_lAudioBuffers.begin(); itBuffer != m_lAudioBuffers.end(); ++itBuffer) { 
     if ((*itBuffer)->isThisFile(strFilename)) { 
      // The Requested File Is Already Loaded Into Memory 
      pAudioBuffer = (*itBuffer); 
      break; 
     } 
    } 

    try { 
     if (nullptr == pAudioBuffer) { 
      // Need To Load The Desired File Into Memory 
      pAudioBuffer.reset(new AudioBuffer(strFilename)); 

      // Store The Buffer 
      m_lAudioBuffers.push_back(pAudioBuffer); 
     } 

     // Create New Source Attached To The Desired Audio Buffer 
     m_mAudioSources[eSoundSource] = std::shared_ptr<AudioSource>(new AudioSource(eSoundSource, pAudioBuffer, bAttachToListener)); 

    } catch (...) { 
     std::ostringstream strStream; 
     strStream << __FUNCTION__ << " failed for SoundSource(" << eSoundSource << ")"; 
     Logger::log(strStream, Logger::TYPE_ERROR); 
    } 
} // createSource 

// ---------------------------------------------------------------------------- 
// Removes Source From Map And If Buffer Is No Longer Needed, 
// It Will Also Be Deleted 
void AudioManager::deleteSource(SoundSource eSoundSource) { 
    BlockThread blockThread(s_csChangeSources); 

    MapAudioSources::iterator it = m_mAudioSources.find(eSoundSource); 
    if (it == m_mAudioSources.end()) { 
     std::ostringstream strStream; 
     strStream << __FUNCTION__ << " could not find SoundSource(" << eSoundSource << ")"; 
     Logger::log(strStream, Logger::TYPE_ERROR); 
     return; // Nothing To Delete 
    } 

    // Get bufferId And Delete Source 
    unsigned uBufferId = it->second->getBufferId(); 
    m_mAudioSources.erase(it); 

    // Find Buffer In List 
    if (uBufferId != INVALID_UNSIGNED) { 
     for (ListAudioBuffers::iterator itBuffer = m_lAudioBuffers.begin(); itBuffer != m_lAudioBuffers.end(); ++itBuffer) { 
      if ((*itBuffer)->getId() == uBufferId) { 
       if ((*itBuffer)->getNumberSourcesAttached() < 1) { 
        // Buffer No Longer Needed 
        m_lAudioBuffers.erase(itBuffer); 
        return; 
       } // If Buffer Not Needed 
      } // If Found Buffer 
     } // For All Buffers 
    } // If Buffer Is Loaded 
} // deleteSource 

// ---------------------------------------------------------------------------- 
// getAudioObject() 
AudioObject* AudioManager::getAudioObject(SoundSource eSoundSource) const { 
    BlockThread blockThread(s_csChangeSources); 

    MapAudioSources::const_iterator it = m_mAudioSources.find(eSoundSource); 
    if (it != m_mAudioSources.cend()) { 
     return it->second.get(); 
    } 

    std::ostringstream strStream; 
    strStream << __FUNCTION__ << " SoundSource(" << eSoundSource << ") has not been found"; 
    Logger::log(strStream, Logger::TYPE_ERROR); 
    return nullptr; 
} // getAudioObject 


} // namespace vmk 

以及使用VolatileLocker的将是如下:注:我这儿也有未示出的OpenglThread类对象,允许对OpenGL的多线程工作。

Game.cpp

#include "Game.h" 
#include "OpenglThread.h" 
#include "VolatileLocker.h" 

... other class object includes 

namespace vmk { 

static CRITICAL_SECTION s_criticalSection; 

// ---------------------------------------------------------------------------- 
// Game() 
Game::Game() : 
Engine(glm::uvec2(3, 3)), 
m_maxSpeechArea(250, INVALID_UNSIGNED), 
m_eLastSpeechSound(SS_INTRO_SHOT), 
m_eSoundSourceToPlay(SS_INTRO_SHOT) { 
    InitializeCriticalSection(&s_criticalSection); 

    const glm::uvec2 gamePixelSize = m_pSettings->getGameSize(); 

    // (0,0) In Top Left Corner Not Bottom Left Which Is The Default 
    m_m4Projection = glm::ortho(0.0f, static_cast<float>(gamePixelSize.x), static_cast<float>(gamePixelSize.y), 0.0f, -10.0f, 10.0f); 

    // Set Background Color 
    glClearColor(22.0f/255.0f, 22.0f/255.0f, 22.0f/255.0f, 1.0f); 

    // Turn Transparencies On 
    glEnable(GL_BLEND); 

    // Initialize Shaders 
    std::string strVertexShader("Shaders/gui.vert"); 
    std::string strFragmentShader("Shaders/gui.frag"); 

    ShaderProgramSettings shaderProgramSettings(P_MAIN, strVertexShader, strFragmentShader); 
    shaderProgramSettings.addVariable(ShaderAttribute(A_POSITION,    AT_FLOAT_VEC2), "inPosition"); 
    shaderProgramSettings.addVariable(ShaderAttribute(A_COLOR,    AT_FLOAT_VEC4), "inColor"); 
    shaderProgramSettings.addVariable(ShaderAttribute(A_TEXTURE_COORD0,  AT_FLOAT_VEC2), "inTextureCoord0"); 

    shaderProgramSettings.addVariable(ShaderUniform(U_MVP_MATRIX,    UT_FLOAT_MAT4), "modelViewProjectionMatrix"); 
    shaderProgramSettings.addVariable(ShaderUniform(U_TEXTURE0_SAMPLER_2D, UT_SAMPLER_2D), "texture0Sampler2d"); 
    shaderProgramSettings.addVariable(ShaderUniform(U_USING_TEXTURE,   UT_BOOL),  "usingTexture"); 
    shaderProgramSettings.addVariable(ShaderUniform(U_ALPHA,     UT_FLOAT),  "inAlpha"); 

    m_pShaderManager->create(shaderProgramSettings); 
    m_pShaderManager->enable(P_MAIN); 

    m_pBatchManager.reset(new BatchManager(10, 10000)); 

    // Must Be Called Before Any GuiElements Are Loaded 
    GuiElement::initialize(); 

    // Load Game Logo - Title Screen 
    m_pTitleScreen = new GuiScreen(std::string("TitleScreen")); 

    TextureFileReader titleTextureFileReader("Assets/images/titleScreen.png"); 
    m_titleTextureInfo = titleTextureFileReader.getOrCreateTextureInfo(TextureInfo::FILTER_NONE, false, false); 

    // Start Worker Thread 
    _beginthread(loadAssets, 0, this); 

    // Game Logo 
    GuiLayoutAbsolute* pTitleCoverLayout = new GuiLayoutAbsolute(glm::ivec2(), Gui::LEFT, m_pSettings->getGameSize(), "title cover"); 
    pTitleCoverLayout->setColor(glm::vec4(0.0862745, 0.0862745, 0.0862745, 1.0)); 
    m_pTitleScreen->addChild(pTitleCoverLayout); 

    m_pTitleLayout = new GuiLayoutAbsolute(glm::ivec2(0, 200), Gui::LEFT, glm::uvec2(955, 400), "title"); 
    m_pTitleLayout->setBackgroundImage(m_titleTextureInfo, glm::uvec2(0, 359), glm::uvec2(955, 400)); 
    m_pTitleLayout->changePriority(1); 
    m_pTitleScreen->addChild(m_pTitleLayout); 

    // Flying Bullet 
    m_pFlyingBulletLayout = new GuiLayoutAbsolute(glm::ivec2(40, -100), "flying bullet"); 
    m_pTitleLayout->addChild(m_pFlyingBulletLayout); 

    // Intro Sound Effect 
    m_pAudioManager->createSource(SS_INTRO_SHOT, "Assets/audio/introShot.ogg"); 
    m_pAudioManager->play(SS_INTRO_SHOT); 

    // Set Timer For How Long Title Screen Should Be Visible 
    m_pAnimationManager->addFunction(5.0, Animation::LINEAR, splashScreenUpdate, this, splashScreenDone, this); 

    /* 
    int debugLogging = m_pSettings->getDebugLogging() | Settings::DEBUG_RENDER; 
    m_pSettings->setDebugLogging(debugLogging); 
    */ 

} // Game 

// ---------------------------------------------------------------------------- 
// ~Game() 
Game::~Game() { 
    DeleteCriticalSection(&s_criticalSection); 
} // ~Game 


// ---------------------------------------------------------------------------- 
// splashScreenDone() 
// Defined Outside Of Game But Is Declared As A Friend Function To Game And It Utilizes The VolatileLocker 
void splashScreenDone(void* pParameter) { 
    Game* pGame = reinterpret_cast<Game*>(pParameter); 
    if (nullptr == pGame) { 
     throw ExceptionHandler(__FUNCTION__ + std::string(" Invalid pParameter passed in")); 
    } 

    VolatileLocker<GameState>(pGame->m_gameState, s_criticalSection)->timerDone(); 

    if (VolatileLocker<GameState>(pGame->m_gameState, s_criticalSection)->is(GameState::PLAYING)) { 
     pGame->m_pAudioManager->play(SS_CHOOSE_LETTER); 
    } 

} // splashScreenDone 

// ---------------------------------------------------------------------------- 
// keyboardInput() 
// A Member Function Of Game That Utilizes The VolatileLocker 
void Game::keyboardInput(unsigned vkCode, bool isPressed) { 
    if (isPressed || VolatileLocker<GameState>(m_gameState, s_criticalSection)->isSplashScreen()) { 
     // Wait For Splash Screen To Be Finished 
     // Only React To Key Release Events 
     return; 
    } 

    static unsigned lastKey = 0; 

    if ((VK_ESCAPE == lastKey && VK_ESCAPE == vkCode) || 
     (VK_ESCAPE == vkCode && m_bGameOver)) { 
     // TODO: Show Credits 

     quitGame(nullptr, nullptr); 
     return; 
    } 
    lastKey = vkCode; 

    if (m_bGameOver) { 
     if (VK_SPACE == vkCode) { 
      restart(); 
     } 
     return; 
    } 

    if (VK_ESCAPE == vkCode) { 
     updateSpeech("To qui the game, press ESC again.", COLOR_YELLOW); 
     speak(SS_QUIT_GAME); 

    } else if (vkCode >= VK_KEYA && vkCode <= VK_KEYZ) { 
     // Play Sound 
     m_pAudioManager->play(SS_GUN_SHOT); 

     updatePuzzle(static_cast<char>(vkCode)); 

     // Show Gun Fire & Start Timer To Reset Graphics Back To Normal 
     showGunFire(true); 
     m_pAnimationManager->addTimer(0.2, resetGunGraphics, this); 

    } else { 
     updateSpeech("Choose a letter."); 
     speak(SS_CHOOSE_LETTER); 
    } 

} // keyboardInput 

} // namespace vmk 

现在我知道在一个线程池或队列工作中提到的OP,但我认为是能够锁定主题为安全起见整体概念可以在这里显示以及。这也可以作为任何人阅读的指南。