0

以下の msdn 記事で概説されている原則と、以下の DXUT ロック フリー パイプ コードに基づいて、単純なロックレス キューを作成しました。

したがって、メイン スレッドがレンダリング命令をフィードし、レンダリング スレッドが利用可能なメッセージを消費して、対応する opengl 呼び出しを発行するプロデューサー/コンシューマー モデルのセットアップがあります。メインスレッドを各ループ/反復で十分な時間スリープさせれば問題なく動作しますが、十分な時間スリープさせない (またはまったくスリープさせない) と、アクセス違反の例外が発生します。

First-chance exception at 0x00b28d9c in Engine.exe: 0xC0000005: Access violation reading location 0x00004104.
Unhandled exception at 0x777715ee in Engine.exe: 0xC0000005: Access violation reading location 0x00004104.

私のコールスタックは次のとおりです。

ntdll.dll!777715ee()    
[Frames below may be incorrect and/or missing, no symbols loaded for ntdll.dll] 
ntdll.dll!777715ee()    
ntdll.dll!7776015e()    
Engine.exe!RingBuffer<2048>::BeginRead(void * & ppMem=, unsigned long & BytesAvailable=)  Line 52 + 0x10 bytes  C++
Engine.exe!Thread::ThreadMain(void * lpParam=0x00107d94)  Line 41 + 0xf bytes   C++

何が問題なのかよくわかりません。ロックレス キューのコードは次のとおりです。

    template <uint32 BufferSize>
    class RingBuffer
    {
    public:
        RingBuffer()
            : m_ReadOffset(0)
            , m_WriteOffset(0)
        {}
        ~RingBuffer()
        {}

        bool Empty() const
        {
            return (m_WriteOffset == m_ReadOffset);
        }

        void BeginRead(void*& ppMem, uint32& BytesAvailable)
        {
            const uint32 ReadOffset = m_ReadOffset;
            const uint32 WriteOffset = m_WriteOffset;

            AppReadWriteBarrier();

            const uint32 Slack =    (WriteOffset > ReadOffset) ?
                            (WriteOffset - ReadOffset) :
                            (ReadOffset > WriteOffset) ?
                                (c_BufferSize - ReadOffset) :
                                (0);

            ppMem = (m_Buffer + ReadOffset);
            BytesAvailable = Slack;
        }

        void EndRead(const uint32 BytesRead)
        {       
            uint32 ReadOffset = m_ReadOffset;

            AppReadWriteBarrier();

            ReadOffset += BytesRead;
            ReadOffset %= c_BufferSize;

            m_ReadOffset = ReadOffset;
        }

        void BeginWrite(void*& ppMem, uint32& BytesAvailable)
        {
            const uint32 ReadOffset = m_ReadOffset;
            const uint32 WriteOffset = m_WriteOffset;

            AppReadWriteBarrier();

            const uint32 Slack =    (WriteOffset > ReadOffset || WriteOffset == ReadOffset) ?
                            (c_BufferSize - WriteOffset) :
                            (ReadOffset - WriteOffset);

            ppMem = (m_Buffer + WriteOffset);
            BytesAvailable = Slack;
        }

        void EndWrite(const uint32 BytesWritten)
        {
            uint32 WriteOffset = m_WriteOffset;

            AppReadWriteBarrier();

            WriteOffset += BytesWritten;
            WriteOffset %= c_BufferSize;

            m_WriteOffset = WriteOffset;
        }

    private:
        const static uint32 c_BufferSize = NEXT_POWER_OF_2(BufferSize);
        const static uint32 c_SizeMask = c_BufferSize - 1;

    private:
        byte8 m_Buffer[ c_BufferSize ];
        volatile ALIGNMENT(4) uint32 m_ReadOffset;
        volatile ALIGNMENT(4) uint32 m_WriteOffset;
    };

読み取り/書き込みオフセットとバッファ ポインタがウォッチ ウィンドウから正常に見えるため、デバッグに苦労しています。残念ながら、アプリが壊れると、BeginRead 関数から autos/local 変数を監視できません。ロックレス プログラミングを使用した経験がある場合は、この問題に関するヘルプや一般的なアドバイスをいただければ幸いです。

4

2 に答える 2

2

これらの記事に興味があるかもしれません...

ロックフリー コード: セキュリティに対する誤った認識
ロックフリー コードの記述: 修正された待ち行列

最初の記事で、Herb Sutter は別の著者によるロックフリー キューの実装について説明し、うまくいかない可能性があることをいくつか指摘しています。2 番目の記事で、Herb は元の実装に対するいくつかの修正を示しています。

学習課題として、独自のロックフリー キューを作成してみるのは非常に良い考えです。しかし、本番環境では、信頼できるソースから既存の実装を見つけてそれを使用する方がおそらく安全です。たとえば、コンカレンシー ランタイムは、concurrent_queueクラスを提供します。

于 2011-08-08T00:29:48.383 に答える
1

メモリ フェンスがありません。volatile 変数へのアクセスは、他の操作ではなく、相互にのみ順序付けられます。

std::atomic<T>C++0x では、 を使用して適切なフェンスを取得できます。それまでは、 Win32 などの OS 固有のスレッド APIInterlockedExchangeや、boost::thread などのラッパー ライブラリが必要になります。

わかりAppReadWriteBarrierました、メモリフェンスを提供するはずです。どのように実装されていますか?

于 2011-08-07T23:53:36.990 に答える