25.09.13, Alice, CircularBuffer, Project
F:\Projects\VS\CircularBufferSln\CircularBufferSln.sln
F:\Projects\VS\CircularBufferSln\CircularBuffer02\CircularBuffer02.csproj
Вот реализация кольцевого буфера (Circular Buffer) на C#:
csharp
public class CircularBuffer<T>
{
private T[] buffer;
private int start = 0; // Индекс начала буфера
private int count = 0;
// Количество элементов в буфере
private readonly int capacity; // Максимальная вместимость
// Конструктор
public CircularBuffer(int size)
{
if (size <= 0)
throw new ArgumentException("Размер буфера должен быть положительным");
capacity = size;
buffer = new T[size];
}
//
Добавление элемента в буфер
public void Enqueue(T item)
{
if (count == capacity)
throw new InvalidOperationException("Буфер переполнен");
int end = (start + count) %
capacity;
buffer[end] = item;
count++;
}
//
Извлечение элемента из буфера
public T Dequeue()
{
if (count == 0)
throw new InvalidOperationException("Буфер пуст");
T item = buffer[start];
start = (start + 1) % capacity;
count--;
return item;
}
//
Получение элемента по индексу
public T Peek(int index)
{
if (index < 0 || index >= count)
throw new IndexOutOfRangeException();
return buffer[(start + index) %
capacity];
}
// Очистка буфера
public void Clear()
{
start = 0;
count = 0;
}
// Текущая длина буфера
public int Count => count;
// Максимальная вместимость
public int Capacity => capacity;
//
Проверка на пустоту
public bool IsEmpty => count == 0;
//
Проверка на заполненность
public bool IsFull => count ==
capacity;
}
Объяснение работы кода
Основные компоненты:
- buffer —
массив для хранения элементов
- start —
индекс начала буфера
- count —
количество элементов в буфере
- capacity —
максимальная вместимость
Методы класса
- Конструктор инициализирует буфер заданного
размера
- Enqueue добавляет элемент в конец буфера
- Dequeue извлекает элемент из начала
буфера
- Peek возвращает элемент по указанному индексу
без удаления
- Clear очищает буфер
- Count возвращает текущее количество
элементов
- Capacity возвращает максимальную
вместимость
- IsEmpty проверяет, пуст ли буфер
- IsFull проверяет, заполнен ли буфер
Пример использования
csharp
var buffer = new CircularBuffer<int>(5);
buffer.Enqueue(1);
buffer.Enqueue(2);
buffer.Enqueue(3);
Console.WriteLine(buffer.Dequeue()); // Выведет 1
Console.WriteLine(buffer.Peek(0)); // Выведет 2
buffer.Enqueue(4);
buffer.Enqueue(5);
buffer.Enqueue(6); // Перезапишет первый элемент
Этот кольцевой буфер эффективно использует память и
обеспечивает постоянную сложность операций добавления и удаления элементов
O(1).
Добавь в код Кольцевого буфера меры синхронизации для
конкурентного использования твоей реализации Кольцевого буфера
Вот реализация потокобезопасного кольцевого буфера на C#:
csharp
using System;
using System.Collections.Generic;
using System.Threading;
public class ThreadSafeCircularBuffer<T>
{
private readonly T[] buffer;
private int start = 0; // Индекс начала буфера
private int count = 0; //
Количество элементов в буфере
private readonly int capacity; // Максимальная вместимость
// Мьютекс для синхронизации доступа
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
// Конструктор
public ThreadSafeCircularBuffer(int size)
{
if (size <= 0)
throw new ArgumentException("Размер буфера должен быть положительным");
capacity = size;
buffer = new T[size];
}
//
Добавление элемента в буфер
public void Enqueue(T item)
{
_lock.EnterWriteLock();
try
{
if (count == capacity)
throw new InvalidOperationException("Буфер переполнен");
int end = (start + count) %
capacity;
buffer[end] = item;
count++;
}
finally
{
_lock.ExitWriteLock();
}
}
//
Извлечение элемента из буфера
public T Dequeue()
{
_lock.EnterWriteLock();
try
{
if (count == 0)
throw new InvalidOperationException("Буфер пуст");
T item = buffer[start];
start = (start + 1) % capacity;
count--;
return item;
}
finally
{
_lock.ExitWriteLock();
}
}
//
Получение элемента по индексу
public T Peek(int index)
{
_lock.EnterReadLock();
try
{
if (index < 0 || index >= count)
throw new IndexOutOfRangeException();
return buffer[(start + index) %
capacity];
}
finally
{
_lock.ExitReadLock();
}
}
// Очистка буфера
public void Clear()
{
_lock.EnterWriteLock();
try
{
start = 0;
count = 0;
}
finally
{
_lock.ExitWriteLock();
}
}
// Текущая длина буфера
public int Count
{
get
{
_lock.EnterReadLock();
try
{
return count;
}
finally
{
_lock.ExitReadLock();
}
}
}
// Максимальная вместимость
public int Capacity => capacity;
//
Проверка на пустоту
public bool IsEmpty
{
get
{
_lock.EnterReadLock();
try
{
return count == 0;
}
finally
{
_lock.ExitReadLock();
}
}
}
//
Проверка на заполненность
public bool IsFull
{
get
{
_lock.EnterReadLock();
try
{
return count == capacity;
}
finally
{
_lock.ExitReadLock();
}
}
}
}
Объяснение добавленных изменений
- ReaderWriterLockSlim — используется для синхронизации
доступа к буферу:
- Позволяет множественный параллельный
доступ для чтения
- Обеспечивает эксклюзивный доступ для
записи
- Блокировки:
- EnterWriteLock()/ExitWriteLock() — для операций модификации
(Enqueue, Dequeue, Clear)
- EnterReadLock()/ExitReadLock() — для операций чтения (Peek,
Count, IsEmpty, IsFull)
- Обработка исключений:
- Использование блока try-finally гарантирует
освобождение блокировки даже при возникновении исключений
Рекомендации по использованию
- При работе с буфером в многопоточной
среде используйте try-catch для обработки возможных
исключений
- Избегайте длительных операций внутри
блокировок
- При необходимости можно добавить
методы с таймаутами для попыток получения блокировки
Пример использования в многопоточной среде
csharp
var buffer = new ThreadSafeCircular
Добавь в Код твоей реализации Кольцевого буфера Два
Итератора. Чтобы можно было бы читать и писать из разных направлений (сторон)
Слева и Справа - Слева IteratorReadFromLeft, IteratorWriteToLeft, - Срава
IteratorReadFromRight, IteratorWriteToRight
Вот реализация кольцевого буфера с поддержкой двустороннего итеративного
доступа:
csharp
using System;
using System.Collections.Generic;
using System.Threading;
public class ThreadSafeCircularBuffer<T>
{
private readonly T[] buffer;
private int start = 0; // Индекс начала буфера
private int count = 0; //
Количество элементов в буфере
private readonly int capacity; // Максимальная вместимость
// Мьютекс для синхронизации доступа
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
//
Индексы для двустороннего доступа
private int leftWriteIndex = 0;
private int rightWriteIndex = 0;
private int leftReadIndex = 0;
private int rightReadIndex = 0;
// Конструктор
public ThreadSafeCircularBuffer(int size)
{
if (size <= 0)
throw new ArgumentException("Размер буфера должен быть положительным");
capacity = size;
buffer = new T[size];
}
//
Добавление элемента в буфер слева
public void EnqueueLeft(T item)
{
_lock.EnterWriteLock();
try
{
if (count == capacity)
throw new InvalidOperationException("Буфер переполнен");
int index = (leftWriteIndex - 1 + capacity) % capacity;
buffer[index] = item;
leftWriteIndex = index;
count++;
}
finally
{
_lock.ExitWriteLock();
}
}
// Добавление элемента в буфер справа
public void EnqueueRight(T item)
{
_lock.EnterWriteLock();
try
{
if (count == capacity)
throw new InvalidOperationException("Буфер переполнен");
int index = rightWriteIndex;
buffer[index] = item;
rightWriteIndex = (index
+ 1) % capacity;
count++;
}
finally
{
_lock.ExitWriteLock();
}
}
// Чтение элемента слева
public T DequeueLeft()
{
_lock.EnterWriteLock();
try
{
if (count == 0)
throw new InvalidOperationException("Буфер пуст");
T item =
buffer[leftWriteIndex];
leftWriteIndex =
(leftWriteIndex + 1) % capacity;
count--;
return item;
}
finally
{
_lock.ExitWriteLock();
}
}
//
Чтение элемента справа
public T DequeueRight()
{
_lock.EnterWriteLock();
try
{
if (count == 0)
throw new InvalidOperationException("Буфер пуст");
int index = (rightWriteIndex - 1 + capacity) % capacity;
T item = buffer[index];
rightWriteIndex = index;
count--;
return item;
}
finally
{
_lock.ExitWriteLock();
}
}
// Итератор для чтения слева
public IEnumerator<T> IteratorReadFromLeft()
{
_lock.EnterReadLock();
try
{
int currentIndex = leftReadIndex;
int elementsRead = 0;
while (elementsRead < count)
{
yield return buffer[currentIndex];
currentIndex =
(currentIndex + 1) % capacity;
elementsRead++;
}
leftReadIndex =
currentIndex;
}
finally
{
_lock.ExitReadLock();
}
}
// Итератор для записи слева
public void IteratorWriteToLeft(IEnumerable<T>
items)
{
_lock.EnterWriteLock();
try
{
foreach (var item in items)
{
if (count == capacity)
throw new InvalidOperationException("Буфер переполнен");
int index = (leftWriteIndex - 1 + capacity) % capacity;
buffer[index] = item;
leftWriteIndex =
index;
count++;
}
}
finally
{
_lock.ExitWriteLock();
}
}
// Итератор для
Напиши теперь примеры использования твоего Применения
Кольцевого Буфера. - Запись слева, Чтение слева, Запись справа, Чтение справа в
разных сочетаниях
Примеры использования кольцевого буфера
Пример 1: Базовая работа с буфером
csharp
var buffer = new ThreadSafeCircularBuffer<string>(5);
// Запись слева и чтение слева
buffer.EnqueueLeft("Левый
элемент 1");
buffer.EnqueueLeft("Левый
элемент 2");
Console.WriteLine(buffer.DequeueLeft());
// Выведет "Левый элемент 2"
Console.WriteLine(buffer.DequeueLeft());
// Выведет "Левый элемент 1"
// Запись справа и чтение справа
buffer.EnqueueRight("Правый
элемент 1");
buffer.EnqueueRight("Правый
элемент 2");
Console.WriteLine(buffer.DequeueRight());
// Выведет "Правый элемент 2"
Console.WriteLine(buffer.DequeueRight());
// Выведет "Правый элемент 1"
Пример 2: Комбинированное использование
csharp
var buffer = new ThreadSafeCircularBuffer<int>(10);
// Заполняем буфер с разных сторон
buffer.EnqueueLeft(1);
buffer.EnqueueLeft(2);
buffer.EnqueueRight(3);
buffer.EnqueueRight(4);
// Читаем слева
Console.WriteLine("Чтение
слева:");
foreach (var item in buffer.IteratorReadFromLeft())
{
Console.WriteLine(item);
}
// Читаем справа
Console.WriteLine("\nЧтение справа:");
buffer.EnqueueRight(5);
buffer.EnqueueRight(6);
int rightItem = buffer.DequeueRight();
Console.WriteLine(rightItem); // Выведет 6
rightItem = buffer.DequeueRight();
Console.WriteLine(rightItem);
// Выведет 5
Пример 3: Массовое добавление элементов
csharp
var buffer = new ThreadSafeCircularBuffer<string>(10);
// Массовое добавление слева
List<string> leftItems = new List<string> { "A", "B", "C" };
buffer.IteratorWriteToLeft(leftItems);
// Массовое добавление справа
List<string> rightItems = new List<string> { "X", "Y", "Z" };
buffer.IteratorWriteToRight(rightItems);
// Чтение всех элементов
Console.WriteLine("Содержимое буфера:");
foreach (var item in buffer.IteratorReadFromLeft())
{
Console.WriteLine(item);
}
Пример 4: Работа в многопоточной среде
csharp
var buffer = new ThreadSafeCircularBuffer<int>(20);
// Поток для записи слева
Task.Run(() =>
{
for (int i = 1; i <= 10; i++)
{
buffer.EnqueueLeft(i);
Thread.Sleep(100);
}
});
// Поток для записи справа
Task.Run(() =>
{
for (int i = 100; i >= 91; i--)
{
buffer.EnqueueRight(i);
Thread.Sleep(150);
}
});
// Поток для чтения слева
Task.Run(() =>
{
Thread.Sleep(500); // Ждем
заполнения буфера
while (!buffer.IsEmpty)
{
Console.WriteLine($"Чтение
слева: {buffer.DequeueLeft()}");
Thread.Sleep(200);
}
});
// Поток для чтения справа
Task.Run(() =>
{
Thread.Sleep(500); // Ждем
заполнения буфера
while (!buffer.IsEmpty)
{
Console.WriteLine($"Чтение
справа: {buffer.DequeueRight()}");
Thread.Sleep(200);
}
});
Console.ReadLine();
Практические сценарии использования
- Буферизация данных в реальном времени:
- Прием данных с датчиков с разных
источников
- Обработка потоковых данных
- Кэширование промежуточных результатов
- Межпоточная коммуникация:
- Очередь сообщений между producer и
consumer
- Буферизация событий в системе
мониторинга
- Синхронизация данных между потоками
- Обработка данных:
- Буферизация при преобразовании данных
- Временное хранение результатов
вычислений
- Кэширование часто используемых
значений
Важные моменты при использовании
- Всегда проверяйте заполненность буфера
перед записью
- Обрабатывайте исключения при
переполнении
- Используйте итераторы для массовой
обработки данных
В данной реализации Переполнения быть не должно. МЫ
должны всегда иметь возможность записать новый элемент Слева или Справа с
удалением противоположного элемента. Пример применения Буфер 1,2,3,4,5 Запись
слева 0 -> 1,2,3,4,5 на выходе должны получить 0,1,2,3,4. Запись справа 6
-> 1,2,3,4,5 на выходе должны получить 2,3,4,5,6 Вот такая должна быть
реализация. Исключения Переполнения быть не должно.
Вот обновленная реализация кольцевого буфера с автоматической заменой
элементов при заполнении:
csharp
using System;
using System.Collections.Generic;
using System.Threading;
public class ThreadSafeCircularBuffer<T>
{
private readonly T[] buffer;
private int start = 0;
private int count = 0;
private readonly int capacity;
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
public ThreadSafeCircularBuffer(int size)
{
if (size <= 0)
throw new ArgumentException("Размер
буфера должен быть положительным");
capacity = size;
buffer = new T[size];
}
//
Запись слева с вытеснением справа
public void EnqueueLeft(T item)
{
_lock.EnterWriteLock();
try
{
int index = (start - 1 + capacity) % capacity;
// Если
буфер полон, сдвигаем начало
if (count == capacity)
start = (start + 1) % capacity;
else
count++;
buffer[index] = item;
start = index;
}
finally
{
_lock.ExitWriteLock();
}
}
// Запись справа с вытеснением слева
public void EnqueueRight(T item)
{
_lock.EnterWriteLock();
try
{
int end = (start + count) %
capacity;
// Если
буфер полон, сдвигаем конец
if (count == capacity)
start = (start + 1) % capacity;
else
count++;
buffer[end] = item;
}
finally
{
_lock.ExitWriteLock();
}
}
// Чтение слева
public T DequeueLeft()
{
_lock.EnterWriteLock();
try
{
if (count == 0)
throw new InvalidOperationException("Буфер пуст");
T item = buffer[start];
start = (start + 1) % capacity;
count--;
return item;
}
finally
{
_lock.ExitWriteLock();
}
}
// Чтение справа
public T DequeueRight()
{
_lock.EnterWriteLock();
try
{
if (count == 0)
throw new InvalidOperationException("Буфер пуст");
int index = (start + count - 1) % capacity;
T item = buffer[index];
count--;
return item;
}
finally
{
_lock.ExitWriteLock();
}
}
// Текущая длина буфера
public int Count
{
get
{
_lock.EnterReadLock();
try
{
return count;
}
finally
{
_lock.ExitReadLock();
}
}
}
// Максимальная вместимость
public int Capacity => capacity;
//
Проверка на пустоту
public bool IsEmpty
{
get
{
_lock.EnterReadLock();
try
{
return count == 0;
}
finally
{
_lock.ExitReadLock();
}
}
}
}
Примеры использования
csharp
// Пример 1: Базовая работа с вытеснением
var buffer = new ThreadSafeCircularBuffer<int>(5);
// Заполняем буфер
for (int i = 1; i <= 5; i++)
buffer.EnqueueRight(i); //
1,2,3,4,5
// Запись слева с вытеснением
buffer.EnqueueLeft(0); //
0,1,2,3,4
// Запись справа с вытеснением
buffer.EnqueueRight(6); // 2,3,4,5,6
// Пример 2: Последовательная запись
var buffer2 = new ThreadSafeCircularBuffer<string>(3);
buffer2.EnqueueRight("A"); // A
buffer2.EnqueueRight("B"); // A,B
buffer2.EnqueueRight("C"); // A,B,C
buffer2.EnqueueRight("D"); //
Может быть я плохо искал. Но в Вашей реализации нет
возможности итерации элементов. Мне еще нужна возможность - итерация элементов
FromLeft - итерация элементов FromRight
Давайте добавим итераторы для обхода буфера слева направо и справа налево.
csharp
using System;
using System.Collections.Generic;
using System.Threading;
public class ThreadSafeCircularBuffer<T>
{
private readonly T[] buffer;
private int start = 0;
private int count = 0;
private readonly int capacity;
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
public ThreadSafeCircularBuffer(int size)
{
if (size <= 0)
throw new ArgumentException("Размер
буфера должен быть положительным");
capacity = size;
buffer = new T[size];
}
//
Запись слева с вытеснением справа
public void EnqueueLeft(T item)
{
_lock.EnterWriteLock();
try
{
int index = (start - 1 + capacity) % capacity;
if (count == capacity)
start = (start + 1) % capacity;
else
count++;
buffer[index] = item;
start = index;
}
finally
{
_lock.ExitWriteLock();
}
}
// Запись справа с вытеснением слева
public void EnqueueRight(T item)
{
_lock.EnterWriteLock();
try
{
int end = (start + count) %
capacity;
if (count == capacity)
start = (start + 1) % capacity;
else
count++;
buffer[end] = item;
}
finally
{
_lock.ExitWriteLock();
}
}
// Итератор слева направо
public IEnumerator<T> IteratorFromLeft()
{
_lock.EnterReadLock();
try
{
int current = start;
int elementsRead = 0;
while (elementsRead < count)
{
yield return buffer[current];
current = (current + 1) % capacity;
elementsRead++;
}
}
finally
{
_lock.ExitReadLock();
}
}
// Итератор справа налево
public IEnumerator<T> IteratorFromRight()
{
_lock.EnterReadLock();
try
{
int current = (start + count - 1) % capacity;
int elementsRead = 0;
while (elementsRead < count)
{
yield return buffer[current];
current = (current - 1 + capacity) % capacity;
elementsRead++;
}
}
finally
{
_lock.ExitReadLock();
}
}
// Остальные методы остаются без изменений...
}
Примеры использования итераторов
csharp
var buffer = new ThreadSafeCircularBuffer<int>(5);
// Заполняем буфер
buffer.EnqueueRight(1);
buffer.EnqueueRight(2);
buffer.EnqueueRight(3);
buffer.EnqueueRight(4);
buffer.EnqueueRight(5);
// Итерация слева направо
Console.WriteLine("Итерация
слева направо:");
foreach (var item in buffer.IteratorFromLeft())
{
Console.Write(item + "
"); //
Выведет: 1 2 3 4 5
}
// Итерация справа налево
Console.WriteLine("\nИтерация
справа налево:");
foreach (var item in buffer.IteratorFromRight())
{
Console.Write(item + "
"); //
Выведет: 5 4 3 2 1
}
// Пример с вытеснением
buffer.EnqueueLeft(0); //
0,1,2,3,4
Console.WriteLine("\nПосле
записи слева:");
foreach (var item in buffer.IteratorFromLeft())
{
Console.Write(item + " "); // Выведет: 0 1 2 3 4
}
buffer.EnqueueRight(6); // 2,3,4,5,6
Console.WriteLine("\nПосле
записи справа:");
foreach (var item in buffer.IteratorFromLeft())
{
Console.Write(item + "
"); //
Выведет: 2 3 4 5 6
}
сделай пож-ста полный текст кода, а то код длинный не
удобно вставлять в разные места, можно ошибиться при вставках. Выложи пож-ста
полный код
Вот полный код реализации кольцевого буфера с итераторами:
csharp
using System;
using System.Collections.Generic;
using System.Threading;
public class ThreadSafeCircularBuffer<T>
{
private readonly T[] buffer;
private int start = 0;
private int count = 0;
private readonly int capacity;
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
// Конструктор
public ThreadSafeCircularBuffer(int size)
{
if (size <= 0)
throw new ArgumentException("Размер буфера должен быть положительным");
capacity = size;
buffer = new T[size];
}
//
Запись слева с вытеснением справа
public void EnqueueLeft(T item)
{
_lock.EnterWriteLock();
try
{
int index = (start - 1 + capacity) % capacity;
if (count == capacity)
start = (start + 1) % capacity;
else
count++;
buffer[index] = item;
start = index;
}
finally
{
_lock.ExitWriteLock();
}
}
// Запись справа с вытеснением слева
public void EnqueueRight(T item)
{
_lock.EnterWriteLock();
try
{
int end = (start + count) %
capacity;
if (count == capacity)
start = (start + 1) % capacity;
else
count++;
buffer[end] = item;
}
finally
{
_lock.ExitWriteLock();
}
}
// Чтение слева
public T DequeueLeft()
{
_lock.EnterWriteLock();
try
{
if (count == 0)
throw new InvalidOperationException("Буфер пуст");
T item = buffer[start];
start = (start + 1) % capacity;
count--;
return item;
}
finally
{
_lock.ExitWriteLock();
}
}
// Чтение справа
public T DequeueRight()
{
_lock.EnterWriteLock();
try
{
if (count == 0)
throw new InvalidOperationException("Буфер пуст");
int index = (start + count - 1) % capacity;
T item = buffer[index];
count--;
return item;
}
finally
{
_lock.ExitWriteLock();
}
}
// Итератор слева направо
public IEnumerator<T> IteratorFromLeft()
{
_lock.EnterReadLock();
try
{
int current = start;
int elementsRead = 0;
while (elementsRead < count)
{
yield return buffer[current];
current = (current + 1) % capacity;
elementsRead++;
}
}
finally
{
_lock.ExitReadLock();
}
}
// Итератор справа налево
public IEnumerator<T> IteratorFromRight()
{
_lock.EnterReadLock();
try
{
int current = (start + count - 1) % capacity;
int elementsRead = 0;
while (elementsRead < count)
{
yield return buffer[current];
current = (current - 1 + capacity) % capacity;
elementsRead++;
}
}
finally
{
_lock.ExitReadLock();
}
}
// Текущая длина буфера
public int Count
{
get
{
_lock.EnterReadLock();
try
{
return count;
}
finally
{
_lock.ExitReadLock();
}
}
}
// Максимальная вместимость
public int Capacity => capacity;
//
Проверка на пустоту
public bool IsEmpty
{
get
{
_lock.EnterReadLock();
try
{
return count == 0;
}
finally
{
Вот на этом месте код оборвался - видимо стоит ограничение на кол-во строк при выводе страницы { get { _lock.EnterReadLock(); try { return count == 0; } finally {
------------------------ Продолжи пож-ста с места // Максимальная вместимость public int Capacity => capacity; // Проверка на пустоту public bool IsEmpty
Вот полный код с продолжением:
csharp
using System;
using System.Collections.Generic;
using System.Threading;
public class ThreadSafeCircularBuffer<T>
{
private readonly T[] buffer;
private int start = 0;
private int count = 0;
private readonly int capacity;
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
// Конструктор
public ThreadSafeCircularBuffer(int size)
{
if (size <= 0)
throw new ArgumentException("Размер буфера должен быть положительным");
capacity = size;
buffer = new T[size];
}
//
Запись слева с вытеснением справа
public void EnqueueLeft(T item)
{
_lock.EnterWriteLock();
try
{
int index = (start - 1 + capacity) % capacity;
if (count == capacity)
start = (start + 1) % capacity;
else
count++;
buffer[index] = item;
start = index;
}
finally
{
_lock.ExitWriteLock();
}
}
// Запись справа с вытеснением слева
public void EnqueueRight(T item)
{
_lock.EnterWriteLock();
try
{
int end = (start + count) %
capacity;
if (count == capacity)
start = (start + 1) % capacity;
else
count++;
buffer[end] = item;
}
finally
{
_lock.ExitWriteLock();
}
}
// Чтение слева
public T DequeueLeft()
{
_lock.EnterWriteLock();
try
{
if (count == 0)
throw new InvalidOperationException("Буфер пуст");
T item = buffer[start];
start = (start + 1) % capacity;
count--;
return item;
}
finally
{
_lock.ExitWriteLock();
}
}
// Чтение справа
public T DequeueRight()
{
_lock.EnterWriteLock();
try
{
if (count == 0)
throw new InvalidOperationException("Буфер пуст");
int index = (start + count - 1) % capacity;
T item = buffer[index];
count--;
return item;
}
finally
{
_lock.ExitWriteLock();
}
}
// Итератор слева направо
public IEnumerator<T> IteratorFromLeft()
{
_lock.EnterReadLock();
try
{
int current = start;
int elementsRead = 0;
while (elementsRead < count)
{
yield return buffer[current];
current = (current + 1) % capacity;
elementsRead++;
}
}
finally
{
_lock.ExitReadLock();
}
}
// Итератор справа налево
public IEnumerator<T> IteratorFromRight()
{
_lock.EnterReadLock();
try
{
int current = (start + count - 1) % capacity;
int elementsRead = 0;
while (elementsRead < count)
{
yield return buffer[current];
current = (current - 1 + capacity) % capacity;
elementsRead++;
}
}
finally
{
_lock.ExitReadLock();
}
}
// Текущая длина буфера
public int Count
{
get
{
_lock.EnterReadLock();
try
{
return count;
}
finally
{
_lock.ExitReadLock();
}
}
}
// Максимальная вместимость
public int Capacity => capacity;
//
Проверка на пустоту
public bool IsEmpty
{
get
{
_lock.EnterReadLock();
try
{
return count == 0;
}
finally
{
_lock.ExitRead
// Максимальная вместимость public int Capacity => capacity; // Проверка на пустоту public bool IsEmpty { get { _lock.EnterReadLock(); try { return count ==
0; } finally { _lock.ExitRead -------- Выложи код с этого места, код оборвался // Максимальная вместимость public int Capacity => capacity; // Проверка на пустоту public bool IsEmpty {
Вот полное завершение класса:
csharp
finally
{
_lock.ExitReadLock();
}
}
}
//
Проверка на заполненность
public bool IsFull
{
get
{
_lock.EnterReadLock();
try
{
return count == capacity;
}
finally
{
_lock.ExitReadLock();
}
}
}
// Очистка буфера
public void Clear()
{
_lock.EnterWriteLock();
try
{
start = 0;
count = 0;
}
finally
{
_lock.ExitWriteLock();
}
}
// Получение элемента по индексу
public T Peek(int index)
{
_lock.EnterReadLock();
try
{
if (index < 0 || index >= count)
throw new IndexOutOfRangeException();
return buffer[(start + index) %
capacity];
}
finally
{
_lock.ExitReadLock();
}
}
}
Теперь класс полностью завершен и включает:
- Все методы работы с буфером
- Проверки на пустоту и заполненность
- Очистку буфера
- Получение элемента по индексу
- Полную синхронизацию доступа через
ReaderWriterLockSlim
Вы можете скопировать весь код целиком и использовать
его в своем проекте.
GetAllFromLeft(IEnumerable[] )
GetAllFromRight(IEnumerable[]) TryGetAllFromLeft(IEnumerable[])
TryGetAllFromRight(IEnumerable[]) Если в реализации таких методов, то
необходимо их реализовать
Вот обновленная версия класса с методами массовой выборки элементов:
csharp
using System;
using System.Collections.Generic;
using System.Threading;
public class ThreadSafeCircularBuffer<T>
{
private readonly T[] buffer;
private int start = 0;
private int count = 0;
private readonly int capacity;
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
// Конструктор
public ThreadSafeCircularBuffer(int size)
{
if (size <= 0)
throw new ArgumentException("Размер буфера должен быть положительным");
capacity = size;
buffer = new T[size];
}
//
Запись слева с вытеснением справа
public void EnqueueLeft(T item)
{
_lock.EnterWriteLock();
try
{
int index = (start - 1 + capacity) % capacity;
if (count == capacity)
start = (start + 1) % capacity;
else
count++;
buffer[index] = item;
start = index;
}
finally
{
_lock.ExitWriteLock();
}
}
// Запись справа с вытеснением слева
public void EnqueueRight(T item)
{
_lock.EnterWriteLock();
try
{
int end = (start + count) %
capacity;
if (count == capacity)
start = (start + 1) % capacity;
else
count++;
buffer[end] = item;
}
finally
{
_lock.ExitWriteLock();
}
}
// Чтение слева
public T DequeueLeft()
{
_lock.EnterWriteLock();
try
{
if (count == 0)
throw new InvalidOperationException("Буфер пуст");
T item = buffer[start];
start = (start + 1) % capacity;
count--;
return item;
}
finally
{
_lock.ExitWriteLock();
}
}
// Чтение справа
public T DequeueRight()
{
_lock.EnterWriteLock();
try
{
if (count == 0)
throw new InvalidOperationException("Буфер пуст");
int index = (start + count - 1) % capacity;
T item = buffer[index];
count--;
return item;
}
finally
{
_lock.ExitWriteLock();
}
}
// Итератор слева направо
public IEnumerator<T> IteratorFromLeft()
{
_lock.EnterReadLock();
try
{
int current = start;
int elementsRead = 0;
while (elementsRead < count)
{
yield return buffer[current];
current = (current + 1) % capacity;
elementsRead++;
}
}
finally
{
_lock.ExitReadLock();
}
}
// Итератор справа налево
public IEnumerator<T> IteratorFromRight()
{
_lock.EnterReadLock();
try
{
int current = (start + count - 1) % capacity;
int elementsRead = 0;
while (elementsRead < count)
{
yield return buffer[current];
current = (current - 1 + capacity) % capacity;
elementsRead++;
}
}
finally
{
_lock.ExitReadLock();
}
}
// Получение всех элементов слева
public void GetAllFromLeft(List<T>
result)
{
_lock.EnterReadLock();
try
{
result.Clear();
int current = start;
for (int i = 0; i < count; i++)
{
result.Add(buffer[current]);
current = (current + 1) % capacity;
}
}
finally
{
_lock.ExitReadLock();
}
}
// Получение всех элементов справа
public void GetAllFromRight(List<
Вот на этом коде обрывается вывод кода, маленкая страница // Получение всех элементов слева public void GetAllFromLeft(List<T> result) { _lock.EnterReadLock();
try { result.Clear(); int current = start; for (int i = 0; i < count; i++) {
result.Add(buffer[current]); current = (current + 1) % capacity; } } finally {
_lock.ExitReadLock(); } } Напиши только эти четыре метода. GetAllFrom Также можешь еще два метода добавить ReSizeFromLeft(int newsize) ReSizeFromRight(int
newsize)
Вот реализация запрошенных методов:
csharp
// Получение всех элементов слева
public void GetAllFromLeft(List<T> result)
{
_lock.EnterReadLock();
try
{
result.Clear();
int current = start;
for (int i = 0; i < count; i++)
{
result.Add(buffer[current]);
current = (current + 1) % capacity;
}
}
finally
{
_lock.ExitReadLock();
}
}
// Получение всех элементов справа
public void GetAllFromRight(List<T> result)
{
_lock.EnterReadLock();
try
{
result.Clear();
int current = (start + count - 1) % capacity;
for (int i = 0; i < count; i++)
{
result.Add(buffer[current]);
current = (current - 1 + capacity) % capacity;
}
}
finally
{
_lock.ExitReadLock();
}
}
// Попытка получения всех элементов слева
(без исключения)
public bool TryGetAllFromLeft(List<T> result)
{
_lock.EnterReadLock();
try
{
if (count == 0)
return false;
result.Clear();
int current = start;
for (int i = 0; i < count; i++)
{
result.Add(buffer[current]);
current = (current + 1) % capacity;
}
return true;
}
finally
{
_lock.ExitReadLock();
}
}
// Попытка получения всех элементов справа
(без исключения)
public bool TryGetAllFromRight(List<T> result)
{
_lock.EnterReadLock();
try
{
if (count == 0)
return false;
result.Clear();
int current = (start + count - 1) % capacity;
for (int i = 0; i < count; i++)
{
result.Add(buffer[current]);
current = (current - 1 + capacity) % capacity;
}
return true;
}
finally
{
_lock.ExitReadLock();
}
}
// Изменение размера буфера с сохранением
элементов слева
public void ReSizeFromLeft(int newSize)
{
if (newSize <= 0)
throw new ArgumentException("Размер должен быть положительным");
_lock.EnterWriteLock();
try
{
T[] newBuffer = new T[newSize];
int elementsToCopy =
Math.Min(count, newSize);
// Копируем элементы слева
int srcIndex = start;
for (int i = 0; i < elementsToCopy; i++)
{
newBuffer[i] =
buffer[srcIndex];
srcIndex = (srcIndex + 1) % capacity;
}
buffer = newBuffer;
start = 0;
count = elementsToCopy;
capacity = newSize;
}
finally
{
_lock.ExitWriteLock();
}
}
// Изменение размера буфера с сохранением
элементов справа
public void ReSizeFromRight(int newSize)
{
if (newSize <= 0)
throw new ArgumentException("Размер должен быть положительным");
_lock.EnterWriteLock();
try
{
T[] newBuffer = new T[newSize];
int elementsToCopy =
Math.Min(count, newSize);
//
Вычисляем новый start для нового буфера
int newStart = newSize -
elementsToCopy;
int srcIndex = (start + count - 1) % capacity;
// Копируем элементы справа
for (int i = newStart; i < newStart
+ elementsToCopy; i++)
{
newBuffer[i] =
buffer[srcIndex];
srcIndex = (srcIndex - 1 + capacity) % capacity;
}
buffer = newBuffer;
start = newStart;
count = elementsToCopy;
capacity = newSize;
}
finally
{
В контексте нашего CircularBuffer уместно ли реализоваить shifts (сдвиги) - с вытеснением - с зацикливанием ?
Давайте рассмотрим, как можно реализовать сдвиги в кольцевом буфере:
Сдвиги с вытеснением
При таком сдвиге элементы смещаются на заданное количество позиций, а
вытесненные элементы удаляются.
Сдвиги с зацикливанием
При таком сдвиге элементы перемещаются циклически, без потери данных.
Вот реализация этих методов:
csharp
// Сдвиг влево с вытеснением
public void ShiftLeft(int positions)
{
_lock.EnterWriteLock();
try
{
if (positions <= 0) return;
positions %= capacity;
start = (start + positions) %
capacity;
count = Math.Min(count -
positions, capacity);
}
finally
{
_lock.ExitWriteLock();
}
}
// Сдвиг вправо с вытеснением
public void ShiftRight(int positions)
{
_lock.EnterWriteLock();
try
{
if (positions <= 0) return;
positions %= capacity;
start = (start - positions +
capacity) % capacity;
count = Math.Min(count -
positions, capacity);
}
finally
{
_lock.ExitWriteLock();
}
}
// Циклический сдвиг влево
public void RotateLeft(int positions)
{
_lock.EnterWriteLock();
try
{
if (positions <= 0 || count == 0) return;
positions %= count;
start = (start + positions) %
capacity;
}
finally
{
_lock.ExitWriteLock();
}
}
// Циклический сдвиг вправо
public void RotateRight(int positions)
{
_lock.EnterWriteLock();
try
{
if (positions <= 0 || count == 0) return;
positions %= count;
start = (start - positions +
capacity) % capacity;
}
finally
{
_lock.ExitWriteLock();
}
}
Объяснение методов:
- ShiftLeft/ShiftRight (с вытеснением):
- Сдвигают элементы на заданное
количество позиций
- Вытесненные элементы удаляются из
буфера
- Размер буфера уменьшается на
количество сдвигов
- RotateLeft/RotateRight (с зацикливанием):
- Перемещают элементы циклически
- Ни один элемент не теряется
- Просто изменяется точка начала буфера
Примеры использования:
csharp
var buffer = new ThreadSafeCircularBuffer<int>(5);
buffer.EnqueueRight(1);
buffer.EnqueueRight(2);
buffer.EnqueueRight(3);
buffer.EnqueueRight(4);
buffer.EnqueueRight(5);
// Сдвиг влево на 2 позиции с вытеснением
buffer.ShiftLeft(2); //
Буфер станет: 3,4,5
// Циклический сдвиг вправо на 1 позицию
buffer.RotateRight(1); //
Буфер станет: 5,3,4
Эти методы делают работу с буфером более гибкой и
позволяют эффективно манипулировать данными без необходимости их явного
копирования.
У нас есть такие функции buffer.GetAllFromLeft(list);
buffer.GetAllFromRight(list); Напрашиваются еще две полезные функции
IEnumerable<T> GetAllFromLeft() IEnumerable<T> GetAllFromRight()
Напиши пож-ста эти две функции, их у нас нет.
Вот реализация методов для получения всех элементов через IEnumerable:
csharp
// Получение всех элементов слева через
IEnumerable
public IEnumerable<T> GetAllFromLeft()
{
_lock.EnterReadLock();
try
{
int current = start;
for (int i = 0; i < count; i++)
{
yield return buffer[current];
current = (current + 1) % capacity;
}
}
finally
{
_lock.ExitReadLock();
}
}
// Получение всех элементов справа через IEnumerable
public IEnumerable<T> GetAllFromRight()
{
_lock.EnterReadLock();
try
{
int current = (start + count - 1) % capacity;
for (int i = 0; i < count; i++)
{
yield return buffer[current];
current = (current - 1 + capacity) % capacity;
}
}
finally
{
_lock.ExitReadLock();
}
}
Эти методы позволяют:
- Получить все элементы буфера в виде
последовательности
- Использовать LINQ-операции
- Работать с элементами через foreach
Примеры использования:
csharp
// Использование с foreach
foreach (var item in buffer.GetAllFromLeft())
{
Console.WriteLine(item);
}
// Использование с LINQ
var result = buffer.GetAllFromRight()
.Where(x => x > 5)
.OrderByDescending(x => x);
// Преобразование в список
List<T> list = buffer.GetAllFromLeft().ToList();
Эти методы дополняют существующий функционал и делают
работу с буфером более гибкой и удобной.
Давай напишем еще две нестандартные функции.
Необходимо сделать Итератор, который делает проходя по индексам петлю. Что это
означает. Допустим у нас capasity = 5 Итератор должен возвращать следующую
последовательность индексов 0,1,2,3,4,3,2,1,0 - получается Петля - .из 9-ти
значений при capasity = 5. 4,2,3,1,0,1,2,3,4 Как всегда Необходимо сделать два
варианта fromLeft, fromRight
Давайте реализуем эти нестандартные итераторы с петлей:
csharp
// Итератор с петлей слева направо
public IEnumerable<T> LoopIteratorFromLeft()
{
_lock.EnterReadLock();
try
{
if (count == 0) yield break;
// Определяем длину петли
int loopLength = 2 * (capacity - 1);
for (int i = 0; i < loopLength; i++)
{
// Вычисляем индекс в буфере
int offset = i < capacity ? i
: loopLength - i;
int index = (start + offset) %
capacity;
yield return buffer[index];
}
}
finally
{
_lock.ExitReadLock();
}
}
// Итератор с петлей справа налево
public IEnumerable<T> LoopIteratorFromRight()
{
_lock.EnterReadLock();
try
{
if (count == 0) yield break;
// Определяем длину петли
int loopLength = 2 * (capacity - 1);
for (int i = 0; i < loopLength; i++)
{
// Вычисляем индекс в буфере
int offset = i < capacity ? i
: loopLength - i;
int index = (start + count - 1 - offset + capacity) % capacity;
yield return buffer[index];
}
}
finally
{
_lock.ExitReadLock();
}
}
Объяснение работы:
- LoopIteratorFromLeft:
- Создает последовательность
длиной 2 * (capacity - 1)
- Сначала идет увеличение индекса до
конца буфера
- Затем уменьшение обратно до начала
- LoopIteratorFromRight:
- Аналогично первому, но движение
начинается с конца
- Сначала идет уменьшение индекса
- Затем увеличение обратно
Примеры работы:
При capacity = 5 и буфере [1,2,3,4,5]:
- LoopIteratorFromLeft вернет:
1,2,3,4,5,4,3,2,1
- LoopIteratorFromRight вернет:
5,4,3,2,1,2,3,4,5
Пример использования:
csharp
var buffer = new ThreadSafeCircularBuffer<int>(5);
buffer.EnqueueRight(1);
buffer.EnqueueRight(2);
buffer.EnqueueRight(3);
buffer.EnqueueRight(4);
buffer.EnqueueRight(5);
// Использование левого итератора с петлей
Console.WriteLine("Левый
итератор с петлей:");
foreach (var item in buffer.LoopIteratorFromLeft())
{
Console.Write(item + "
"); //
Выведет: 1 2 3 4 5 4 3 2 1
}
// Использование правого итератора с
петлей
Console.WriteLine("\nПравый
итератор с петлей:");
foreach (var item in buffer.LoopIteratorFromRight())
{
Console.Write(item + "
"); //
Выведет: 5 4 3 2 1 2 3 4 5
}
Эти методы добавляют уникальный способ обхода буфера,
который может быть полезен в специфических задачах обработки данных.
ПОзволяет рисовать симметриччные формы
Давайте адаптируем эти итераторы для создания симметричных форм.
Вот улучшенная версия с дополнительными комментариями:
csharp
// Итератор с петлей слева направо для
создания симметричных форм
public IEnumerable<T> LoopIteratorFromLeft()
{
_lock.EnterReadLock();
try
{
if (count == 0) yield break;
// Длина петли всегда 2 * (capacity - 1)
int loopLength = 2 * (capacity - 1);
for (int i = 0; i < loopLength; i++)
{
//
Вычисляем смещение: сначала растет, потом уменьшается
int offset = i < capacity ? i
: loopLength - i;
//
Вычисляем индекс в буфере с учетом начала
int index = (start + offset) %
capacity;
yield return buffer[index];
}
}
finally
{
_lock.ExitReadLock();
}
}
// Итератор с петлей справа налево для
создания симметричных форм
public IEnumerable<T> LoopIteratorFromRight()
{
_lock.EnterReadLock();
try
{
if (count == 0) yield break;
// Длина петли всегда 2 * (capacity - 1)
int loopLength = 2 * (capacity - 1);
for (int i = 0; i < loopLength; i++)
{
//
Вычисляем смещение: сначала уменьшается, потом растет
int offset = i < capacity ? i
: loopLength - i;
// Вычисляем индекс с конца буфера
int index = (start + count - 1 - offset + capacity) % capacity;
yield return buffer[index];
}
}
finally
{
_lock.ExitReadLock();
}
}
Примеры использования для рисования:
- Создание симметричных узоров:
csharp
// Пример с цветами для градиента
var colors = new ThreadSafeCircularBuffer<Color>(5);
colors.EnqueueRight(Color.Red);
colors.EnqueueRight(Color.Orange);
colors.EnqueueRight(Color.Yellow);
colors.EnqueueRight(Color.Green);
colors.EnqueueRight(Color.Blue);
// Рисование симметричного узора
foreach (var color in colors.LoopIteratorFromLeft())
{
DrawShape(color); //
Метод рисования фигуры текущим цветом
}
- Создание симметричных паттернов:
csharp
// Пример с размерами для создания узора
var sizes = new ThreadSafeCircularBuffer<int>(5);
sizes.EnqueueRight(10);
sizes.EnqueueRight(20);
sizes.EnqueueRight(30);
sizes.EnqueueRight(40);
sizes.EnqueueRight(50);
// Создание симметричного паттерна
foreach (var size in sizes.LoopIteratorFromRight())
{
DrawCircle(size); //
Метод рисования круга заданного размера
}
Особенности использования для графики:
- Плавные переходы: идеально подходят для создания
градиентов
- Симметричные узоры: автоматически создают зеркальные
паттерны
- Циклические формы: позволяют создавать повторяющиеся
симметричные элементы
- Экономия ресурсов: не требуют дополнительного хранения
данных
Эти итераторы особенно полезны при:
- Создании графических эффектов
- Анимации с симметричными переходами
- Генерации паттернов
- Визуализации данных с симметрией
Давайте теперь подведем некоторые итоги. Выведи
пож-ста сигнатуры всех наших функций класса ThreadSafeCircularBuffer. и
конструктор/ры этого класса. Надо посмотреть, что у нас получилось в целом.
Вот полный список публичных методов и конструкторов класса ThreadSafeCircularBuffer:
Конструкторы
csharp
public ThreadSafeCircularBuffer(int size)
Основные операции
csharp
// Добавление элементов
public void EnqueueLeft(T item)
public void EnqueueRight(T item)
// Удаление элементов
public T DequeueLeft()
public T DequeueRight()
Итераторы и получение элементов
csharp
// Стандартные итераторы
public IEnumerator<T> IteratorFromLeft()
public IEnumerator<T> IteratorFromRight()
// Получение всех элементов в список
public void GetAllFromLeft(List<T> result)
public void GetAllFromRight(List<T> result)
// Получение через IEnumerable
public IEnumerable<T> GetAllFromLeft()
public IEnumerable<T> GetAllFromRight()
// Специальные итераторы с петлей
public IEnumerable<T> LoopIteratorFromLeft()
public IEnumerable<T> LoopIteratorFromRight()
Массовая обработка
csharp
// Массовое получение элементов
public void GetAllFromLeft(List<T> result)
public void GetAllFromRight(List<T> result)
public bool TryGetAllFromLeft(List<T> result)
public bool TryGetAllFromRight(List<T> result)
Изменение размера
csharp
// Изменение размера буфера
public void ReSizeFromLeft(int newSize)
public void ReSizeFromRight(int newSize)
Сдвиги элементов
csharp
// Сдвиги с вытеснением
public void ShiftLeft(int positions)
public void ShiftRight(int positions)
// Циклические сдвиги
public void RotateLeft(int positions)
public void RotateRight(int positions)
Свойства и проверки
csharp
// Базовые свойства
public int Count { get; }
public int Capacity { get; }
// Проверки состояния
public bool IsEmpty { get; }
public bool IsFull { get; }
// Получение элемента по индексу
public T Peek(int index)
Дополнительные методы
csharp
// Очистка буфера
public void Clear()
Особенности реализации
- Полная потокобезопасность через ReaderWriterLockSlim
- Поддержка generic-типа T
- Автоматическая обработка кольцевого
буфера
- Эффективное использование памяти
Класс предоставляет полный набор инструментов для работы с кольцевым
буфером, включая:
- Базовые операции добавления/удаления
- Различные способы итерации
- Массовую обработку данных
- Гибкую работу с размером
- Безопасную многопоточную работу
Комментариев нет:
Отправить комментарий