Нарушение прав доступа в очереди без блокировки в многопоточном приложении - PullRequest
0 голосов
/ 08 августа 2011

Я написал простую очередь без блокировки, основанную на принципах, изложенных в статье 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;
    };

У меня проблемы с отладкой, так как смещения чтения / записи и указатель буфера выглядят нормально из окна просмотра. К сожалению, когда приложение ломается, я не могу смотреть autos / local переменные из функции BeginRead. Если у кого-то есть опыт работы с программированием без блокировки, любая помощь по этой проблеме или советы в целом будут очень важны.

Ответы [ 2 ]

2 голосов
/ 08 августа 2011

Эти статьи могут вас заинтересовать ...

Код без блокировки: ложное чувство безопасности
Написание кода без блокировки: исправленоОчередь

В первой статье Херб Саттер обсуждает реализацию очереди без блокировки другого автора и указывает на некоторые вещи, которые могут пойти не так.Во второй статье Херб показывает некоторые исправления в исходной реализации.

В качестве учебного упражнения попытка создать собственную очередь без блокировки - довольно хорошая идея.Но для производственной работы вам, вероятно, будет безопаснее найти уже существующую реализацию из надежного источника и использовать ее.Например, Concurrency Runtime предлагает concurrent_queue class

1 голос
/ 08 августа 2011

У вас нет никаких заборов памяти. Доступ к переменным переменным упорядочен только по отношению друг к другу, а не к другим операциям.

В C ++ 0x вы сможете использовать std::atomic<T> для получения соответствующих заборов. До этого вам понадобятся специфичные для ОС API-интерфейсы потоков, такие как Win32 InterlockedExchange, или библиотека-обертка, такая как boost :: thread.

Хорошо, я вижу, что AppReadWriteBarrier должен обеспечить защиту памяти. Как это реализовано?

...