Мой простой круговой буфер работает неправильно

Я пытаюсь реализовать простой круговой буфер.

class MyCircularBuffer
{
    byte[] Buffer = null;

    private Object BufferLock = new object();

    int readIndex = 0;
    int writeIndex = 0;

    int SpaceBetweenIndexes
    {
        get;
        set;
    }

    int ReadIndex
    {
        get
        {
            return readIndex;
        }
        set
        {
            readIndex = value;
            CalculateSpaceBetweenIndexes();
        }
    }

    void CalculateSpaceBetweenIndexes()
    {
        if (readIndex <= writeIndex)
        {
            SpaceBetweenIndexes = writeIndex - readIndex;
        }
        else
        {
            SpaceBetweenIndexes = Length - (readIndex - writeIndex);
        }
    }

    int WriteIndex
    {
        get
        {
            return writeIndex;
        }
        set
        {
            writeIndex = value;
            CalculateSpaceBetweenIndexes();
        }
    }

    int Length;

    int Count
    {
        get
        {
            lock(BufferLock)
            {
                return Buffer.Count();
            }
        }
    }

    public MyCircularBuffer(int length)
    {
        Buffer = new byte[length];
        this.Length = length;
        ReadIndex = 0;
        WriteIndex = 0;
    }

    public byte ReadByte()
    {
        while (SpaceBetweenIndexes <= 1)
        {
            CalculateSpaceBetweenIndexes();
        }

        ReadIndex++;

        if (ReadIndex < Length)
        {
            Console.WriteLine("Read from index " + (ReadIndex-1) + " value " + this[ReadIndex - 1]);
            return this[ReadIndex - 1];
        }
        else
        {
            ReadIndex = 0;
            Console.WriteLine("Read from index " + (Length-1) + " value " + this[Length-1]);
            return this[Length-1];
        }
    }

    public void WriteByte(byte value)
    {
        WriteIndex++;
        
        if (WriteIndex < Length)
        {
            Console.WriteLine("Wrote from index " + (WriteIndex-1) + " value " + value);
            this[WriteIndex - 1] = value;
        }
            
        else if(writeIndex == Length)
        {
            Console.WriteLine("Wrote from index " + (WriteIndex-1) + " value " + value);
            this[WriteIndex-1] = value;

            WriteIndex = 0;
        }
    }

    public byte this[int index]
    {
        get
        {
            lock(BufferLock)
            {
                return Buffer[index];
            }  
        }
        set
        {
            lock(BufferLock)
            {
                Buffer[index] = value;
            } 
        }
    }
}

Код для генерации теста:

class Program
{


    public static  MyCircularBuffer buff = new MyCircularBuffer(10);

    static void Main(string[] args)
    {
        

        System.Threading.Thread t1 = new System.Threading.Thread(new System.Threading.ThreadStart(DrawByte));
        System.Threading.Thread t2 = new System.Threading.Thread(new System.Threading.ThreadStart(WriteByte));

       
        t2.Start();

        t1.Start();


        Console.ReadKey();
    }

    public static void DrawByte()
    {
        Random rand = new Random(50);

        for (int i = 0; i < 20; i++)
        {
            buff.WriteByte((byte)(rand.Next()+1));
        }
    }

    public static void WriteByte()
    {
        for (int i = 0; i < 20; i++)
        {
            buff.ReadByte(); //In this example my class is showing the results, so I didn't write here Console.Writeline...
        }
    }
}

Первый поток будет использовать метод ReadByte для получения байтов из этого буфера, а второй поток будет использовать метод WriteByte для записи байтов в буфер. У меня есть writeindex и readindex, которые перемещаются по буферу. Вроде бы все работает хорошо, но это:

ошибка

Есть идеи/советы? Надеюсь, вы мне поможете, ребята!


person Terrykk    schedule 08.10.2015    source источник
comment
Возможно, вам также следует добавить код для создания тестового примера, чтобы люди, отвечающие на вопрос, могли протестировать свое решение.   -  person Willem Van Onsem    schedule 08.10.2015
comment
Вы должны добавить блокировку вокруг методов readByte() и writeByte()   -  person Domysee    schedule 08.10.2015
comment
Что должно произойти, когда читатель будет отставать от писателя или наоборот? Должны ли они ждать друг друга?   -  person Anton Gogolev    schedule 08.10.2015
comment
Писатель @AntonGogolev всегда должен быть выше, но не более чем на один круг, читатель должен ждать новых данных, если он находится в том же положении, что и писатель.   -  person Terrykk    schedule 08.10.2015
comment
@CommuSoft хорошо, я добавляю это в пост   -  person Terrykk    schedule 08.10.2015
comment
Ваши блокировки слишком узкие - вы блокируете только операции с буфером, а другие переменные с состоянием изменяются вне блокировок. Попробуйте заблокировать каждый метод и посмотрите, решит ли это проблему.   -  person Enigmativity    schedule 08.10.2015
comment
@Доминик почему? У меня есть блокировка для получения и установки буфера. Этого не достаточно?   -  person Terrykk    schedule 08.10.2015
comment
@Terrykk - вам нужно заблокировать все состояние, а не только буфер.   -  person Enigmativity    schedule 08.10.2015
comment
Должен ли это быть тот же замок или мне нужно создать другой?   -  person Terrykk    schedule 08.10.2015
comment
@Terrykk Чтобы избежать условий гонки. Рассмотрим случай, когда у вас есть 2 потока, оба хотят писать. Первые потоки выполняют WriteIndex++, затем планировщик переходит к следующему потоку, который также выполняет WriteIndex++. Потом оба потока пишут. Итак, теперь оба потока записывают в одну и ту же позицию, и у вас есть неверные данные.   -  person Domysee    schedule 08.10.2015
comment
@Dominik хорошо, я это знаю, но мне нужно написать только одну тему и одну прочитать. Это хороший совет от вас, но я думаю, что это не решение моей проблемы.   -  person Terrykk    schedule 08.10.2015
comment
@Terrykk, наверное, нет, но тем не менее, вы пробовали?   -  person Domysee    schedule 08.10.2015
comment
да, я пробовал, мне не помогло, но спасибо. @yue shi заметил, что у меня нет условий, чтобы заставить автора ждать читателя, и я пытаюсь решить эту проблему.   -  person Terrykk    schedule 08.10.2015
comment
@Terrykk не существует такой вещи, как простой циклический буфер, который также является потокобезопасным и реализует шаблон производитель-потребитель. Вместо того, чтобы тратить свое время, лучше посмотрите на System.Collections.Concurrent и выберите то, что лучше всего соответствует вашим потребностям.   -  person Ivan Stoev    schedule 08.10.2015


Ответы (1)


Жаль, что у меня не было достаточно "Репутации", чтобы поставить это, чтобы прокомментировать. В любом случае, почему вы думаете, что есть проблема? Разве в выделенном вами красном поле не ждала читательская ветка? Как только у него появилась возможность запуститься, он выбрал индекс 8, что было правильным значением.

person yue shi    schedule 08.10.2015
comment
Проблема в том, что ридер пропустил целый круг. Например, писатель записал 1 2 3 4 5 6 7 8 9 10 11 в буфер длиной 5, поэтому читатель прочитал 1,2,3,4,5, 11 (поэтому пропущены 6,7,8,9,10) - person Terrykk; 08.10.2015
comment
Что заставляет писателя ждать читателя? Writer может продолжать записывать в кольцевой буфер, когда читатель получает возможность запустить его, он просто получает значение в ReadIndex. В данном случае это 0, значение индекса 0 равно 11. Я что-то упустил? - person yue shi; 08.10.2015
comment
о, это глупо, но у меня нет ничего, что заставило бы писателя ждать читателя. Условия, которые я написал, для этого недостаточны, спасибо. попробую что-нибудь с этим сделать - person Terrykk; 08.10.2015